Necesito reemplazar algunos caracteres de la siguiente manera: &
➔ \&
, #
➔ \#
, ...
Codifiqué lo siguiente, pero supongo que debería haber una mejor manera. ¿Alguna pista?
strs = strs.replace('&', '\&')
strs = strs.replace('#', '\#')
...
Necesito reemplazar algunos caracteres de la siguiente manera: &
➔ \&
, #
➔ \#
, ...
Codifiqué lo siguiente, pero supongo que debería haber una mejor manera. ¿Alguna pista?
strs = strs.replace('&', '\&')
strs = strs.replace('#', '\#')
...
Respuestas:
Tomé el tiempo de todos los métodos en las respuestas actuales junto con uno extra.
Con una cadena de entrada de abc&def#ghi
y reemplazando y -> \ & y # -> \ #, la manera más rápida era encadenar los reemplazos como este: text.replace('&', '\&').replace('#', '\#')
.
Tiempos para cada función:
Aquí están las funciones:
def a(text):
chars = "&#"
for c in chars:
text = text.replace(c, "\\" + c)
def b(text):
for ch in ['&','#']:
if ch in text:
text = text.replace(ch,"\\"+ch)
import re
def c(text):
rx = re.compile('([&#])')
text = rx.sub(r'\\\1', text)
RX = re.compile('([&#])')
def d(text):
text = RX.sub(r'\\\1', text)
def mk_esc(esc_chars):
return lambda s: ''.join(['\\' + c if c in esc_chars else c for c in s])
esc = mk_esc('&#')
def e(text):
esc(text)
def f(text):
text = text.replace('&', '\&').replace('#', '\#')
def g(text):
replacements = {"&": "\&", "#": "\#"}
text = "".join([replacements.get(c, c) for c in text])
def h(text):
text = text.replace('&', r'\&')
text = text.replace('#', r'\#')
def i(text):
text = text.replace('&', r'\&').replace('#', r'\#')
Programado así:
python -mtimeit -s"import time_functions" "time_functions.a('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.b('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.c('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.d('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.e('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.f('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.g('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.h('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.i('abc&def#ghi')"
Aquí hay un código similar para hacer lo mismo pero con más caracteres para escapar (\ `* _ {}> # + -.! $):
def a(text):
chars = "\\`*_{}[]()>#+-.!$"
for c in chars:
text = text.replace(c, "\\" + c)
def b(text):
for ch in ['\\','`','*','_','{','}','[',']','(',')','>','#','+','-','.','!','$','\'']:
if ch in text:
text = text.replace(ch,"\\"+ch)
import re
def c(text):
rx = re.compile('([&#])')
text = rx.sub(r'\\\1', text)
RX = re.compile('([\\`*_{}[]()>#+-.!$])')
def d(text):
text = RX.sub(r'\\\1', text)
def mk_esc(esc_chars):
return lambda s: ''.join(['\\' + c if c in esc_chars else c for c in s])
esc = mk_esc('\\`*_{}[]()>#+-.!$')
def e(text):
esc(text)
def f(text):
text = text.replace('\\', '\\\\').replace('`', '\`').replace('*', '\*').replace('_', '\_').replace('{', '\{').replace('}', '\}').replace('[', '\[').replace(']', '\]').replace('(', '\(').replace(')', '\)').replace('>', '\>').replace('#', '\#').replace('+', '\+').replace('-', '\-').replace('.', '\.').replace('!', '\!').replace('$', '\$')
def g(text):
replacements = {
"\\": "\\\\",
"`": "\`",
"*": "\*",
"_": "\_",
"{": "\{",
"}": "\}",
"[": "\[",
"]": "\]",
"(": "\(",
")": "\)",
">": "\>",
"#": "\#",
"+": "\+",
"-": "\-",
".": "\.",
"!": "\!",
"$": "\$",
}
text = "".join([replacements.get(c, c) for c in text])
def h(text):
text = text.replace('\\', r'\\')
text = text.replace('`', r'\`')
text = text.replace('*', r'\*')
text = text.replace('_', r'\_')
text = text.replace('{', r'\{')
text = text.replace('}', r'\}')
text = text.replace('[', r'\[')
text = text.replace(']', r'\]')
text = text.replace('(', r'\(')
text = text.replace(')', r'\)')
text = text.replace('>', r'\>')
text = text.replace('#', r'\#')
text = text.replace('+', r'\+')
text = text.replace('-', r'\-')
text = text.replace('.', r'\.')
text = text.replace('!', r'\!')
text = text.replace('$', r'\$')
def i(text):
text = text.replace('\\', r'\\').replace('`', r'\`').replace('*', r'\*').replace('_', r'\_').replace('{', r'\{').replace('}', r'\}').replace('[', r'\[').replace(']', r'\]').replace('(', r'\(').replace(')', r'\)').replace('>', r'\>').replace('#', r'\#').replace('+', r'\+').replace('-', r'\-').replace('.', r'\.').replace('!', r'\!').replace('$', r'\$')
Aquí están los resultados para la misma cadena de entrada abc&def#ghi
:
Y con una cadena de entrada más larga ( ## *Something* and [another] thing in a longer sentence with {more} things to replace$
):
Agregar un par de variantes:
def ab(text):
for ch in ['\\','`','*','_','{','}','[',']','(',')','>','#','+','-','.','!','$','\'']:
text = text.replace(ch,"\\"+ch)
def ba(text):
chars = "\\`*_{}[]()>#+-.!$"
for c in chars:
if c in text:
text = text.replace(c, "\\" + c)
Con la entrada más corta:
Con la entrada más larga:
Así que voy a usar ba
para facilitar la lectura y la velocidad.
Impulsado por los hacks en los comentarios, una diferencia entre ab
y ba
es el if c in text:
cheque. Probémoslos contra dos variantes más:
def ab_with_check(text):
for ch in ['\\','`','*','_','{','}','[',']','(',')','>','#','+','-','.','!','$','\'']:
if ch in text:
text = text.replace(ch,"\\"+ch)
def ba_without_check(text):
chars = "\\`*_{}[]()>#+-.!$"
for c in chars:
text = text.replace(c, "\\" + c)
Los tiempos en μs por ciclo en Python 2.7.14 y 3.6.3, y en una máquina diferente del conjunto anterior, por lo que no se pueden comparar directamente.
╭────────────╥──────┬───────────────┬──────┬──────────────────╮
│ Py, input ║ ab │ ab_with_check │ ba │ ba_without_check │
╞════════════╬══════╪═══════════════╪══════╪══════════════════╡
│ Py2, short ║ 8.81 │ 4.22 │ 3.45 │ 8.01 │
│ Py3, short ║ 5.54 │ 1.34 │ 1.46 │ 5.34 │
├────────────╫──────┼───────────────┼──────┼──────────────────┤
│ Py2, long ║ 9.3 │ 7.15 │ 6.85 │ 8.55 │
│ Py3, long ║ 7.43 │ 4.38 │ 4.41 │ 7.02 │
└────────────╨──────┴───────────────┴──────┴──────────────────┘
Podemos concluir que:
Aquellos con el cheque son hasta 4 veces más rápidos que aquellos sin el cheque
ab_with_check
está ligeramente a la cabeza en Python 3, pero ba
(con verificación) tiene una mayor ventaja en Python 2
Sin embargo, la lección más importante aquí es que Python 3 es hasta 3 veces más rápido que Python 2 . ¡No hay una gran diferencia entre el más lento en Python 3 y el más rápido en Python 2!
if c in text:
necesario en ba
?
1.45 usec per loop
y sin: 5.3 usec per loop
, larga cadena, con: 4.38 usec per loop
y sin que: 7.03 usec per loop
. (Tenga en cuenta que estos no son directamente comparables con los resultados anteriores, porque es una máquina diferente, etc.)
replace
solo se llama cuando c
se encuentra text
en caso de ba
que se llame en cada iteración ab
.
>>> string="abc&def#ghi"
>>> for ch in ['&','#']:
... if ch in string:
... string=string.replace(ch,"\\"+ch)
...
>>> print string
abc\&def\#ghi
string=string.replace(ch,"\\"+ch)
? ¿No es string.replace(ch,"\\"+ch)
suficiente?
Simplemente encadene las replace
funciones como esta
strs = "abc&def#ghi"
print strs.replace('&', '\&').replace('#', '\#')
# abc\&def\#ghi
Si los reemplazos van a ser más numerosos, puede hacerlo de esta manera genérica
strs, replacements = "abc&def#ghi", {"&": "\&", "#": "\#"}
print "".join([replacements.get(c, c) for c in strs])
# abc\&def\#ghi
Aquí hay un método python3 que usa str.translate
y str.maketrans
:
s = "abc&def#ghi"
print(s.translate(str.maketrans({'&': '\&', '#': '\#'})))
La cadena impresa es abc\&def\#ghi
.
.translate()
parece ser más lento que tres encadenados .replace()
(usando CPython 3.6.4).
replace()
mí mismo, pero agregué esta respuesta en aras de la exhaustividad.
'\#'
válido? no debería ser r'\#'
o '\\#'
? Podría ser un problema de formato de bloque de código, tal vez.
¿Siempre vas a anteponer una barra invertida? Si es así, intente
import re
rx = re.compile('([&#])')
# ^^ fill in the characters here.
strs = rx.sub('\\\\\\1', strs)
Puede que no sea el método más eficiente, pero creo que es el más fácil.
r'\\\1'
Tarde a la fiesta, pero perdí mucho tiempo con este problema hasta que encontré mi respuesta.
Corto y dulce, translate
es superior areplace
. Si está más interesado en la optimización de la funcionalidad con el tiempo, no usereplace
.
Úselo también translate
si no sabe si el conjunto de caracteres que se reemplazará se superpone al conjunto de caracteres utilizado para reemplazar.
Caso en punto:
Usarlo replace
ingenuamente esperaría que el fragmento "1234".replace("1", "2").replace("2", "3").replace("3", "4")
regrese "2344"
, pero de hecho volverá "4444"
.
La traducción parece realizar lo que OP originalmente deseaba.
Puede considerar escribir una función de escape genérica:
def mk_esc(esc_chars):
return lambda s: ''.join(['\\' + c if c in esc_chars else c for c in s])
>>> esc = mk_esc('&#')
>>> print esc('Learn & be #1')
Learn \& be \#1
De esta manera, puede configurar su función con una lista de caracteres que se deben escapar.
Para su información, esto es de poca o ninguna utilidad para el OP, pero puede ser útil para otros lectores (por favor, no desestime, estoy al tanto de esto).
Como ejercicio algo ridículo pero interesante, quería ver si podía usar la programación funcional de Python para reemplazar múltiples caracteres. Estoy bastante seguro de que esto NO supera simplemente llamando a replace () dos veces. Y si el rendimiento fuera un problema, podría superarlo fácilmente en rust, C, julia, perl, java, javascript y quizás incluso awk. Utiliza un paquete externo de 'ayudantes' llamado pytoolz , acelerado a través de cython ( cytoolz, es un paquete pypi ).
from cytoolz.functoolz import compose
from cytoolz.itertoolz import chain,sliding_window
from itertools import starmap,imap,ifilter
from operator import itemgetter,contains
text='&hello#hi&yo&'
char_index_iter=compose(partial(imap, itemgetter(0)), partial(ifilter, compose(partial(contains, '#&'), itemgetter(1))), enumerate)
print '\\'.join(imap(text.__getitem__, starmap(slice, sliding_window(2, chain((0,), char_index_iter(text), (len(text),))))))
Ni siquiera voy a explicar esto porque nadie se molestaría en usar esto para lograr un reemplazo múltiple. Sin embargo, me sentí algo logrado al hacer esto y pensé que podría inspirar a otros lectores o ganar un concurso de ofuscación de código.
Usando reduce, que está disponible en python2.7 y python3. *, Puede reemplazar fácilmente las subcadenas múltiples de una manera limpia y pitónica.
# Lets define a helper method to make it easy to use
def replacer(text, replacements):
return reduce(
lambda text, ptuple: text.replace(ptuple[0], ptuple[1]),
replacements, text
)
if __name__ == '__main__':
uncleaned_str = "abc&def#ghi"
cleaned_str = replacer(uncleaned_str, [("&","\&"),("#","\#")])
print(cleaned_str) # "abc\&def\#ghi"
En python2.7 no tiene que importar reduce, pero en python3. * Debe importarlo desde el módulo functools.
Tal vez un bucle simple para reemplazar los caracteres:
a = '&#'
to_replace = ['&', '#']
for char in to_replace:
a = a.replace(char, "\\"+char)
print(a)
>>> \&\#