Tomé el tiempo de todos los métodos en las respuestas actuales junto con uno extra.
Con una cadena de entrada de abc&def#ghiy reemplazando y -> \ & y # -> \ #, la manera más rápida era encadenar los reemplazos como este: text.replace('&', '\&').replace('#', '\#').
Tiempos para cada función:
a) 1000000 bucles, lo mejor de 3: 1.47 μs por bucle
b) 1000000 bucles, lo mejor de 3: 1.51 μs por bucle
c) 100000 bucles, lo mejor de 3: 12.3 μs por bucle
d) 100000 bucles, lo mejor de 3: 12 μs por bucle
e) 100000 bucles, lo mejor de 3: 3.27 μs por bucle
f) 1000000 bucles, lo mejor de 3: 0.817 μs por bucle
g) 100000 bucles, lo mejor de 3: 3.64 μs por bucle
h) 1000000 bucles, lo mejor de 3: 0.927 μs por bucle
i) 1000000 bucles, lo mejor de 3: 0.814 μs por bucle
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):returnlambda 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'\#')
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):returnlambda 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:
a) 100000 bucles, lo mejor de 3: 6,72 μs por bucle
b) 100000 bucles, lo mejor de 3: 2.64 μs por bucle
c) 100000 bucles, lo mejor de 3: 11.9 μs por bucle
d) 100000 bucles, lo mejor de 3: 4,92 μs por bucle
mi) 100000 bucles, lo mejor de 3: 2.96 μs por bucle
f) 100000 bucles, lo mejor de 3: 4.29 μs por bucle
g) 100000 bucles, lo mejor de 3: 4.68 μs por bucle
h) 100000 bucles, lo mejor de 3: 4,73 μs por bucle
i) 100000 bucles, lo mejor de 3: 4.24 μs por bucle
Y con una cadena de entrada más larga ( ## *Something* and [another] thing in a longer sentence with {more} things to replace$):
a) 100000 bucles, lo mejor de 3: 7,59 μs por bucle
b) 100000 bucles, lo mejor de 3: 6.54 μs por bucle
c) 100000 bucles, lo mejor de 3: 16.9 μs por bucle
d) 100000 bucles, lo mejor de 3: 7.29 μs por bucle
e) 100000 bucles, lo mejor de 3: 12.2 μs por bucle
f) 100000 bucles, lo mejor de 3: 5.38 μs por bucle
g) 10000 bucles, lo mejor de 3: 21.7 μs por bucle
h) 100000 bucles, lo mejor de 3: 5,7 μs por bucle
i) 100000 bucles, lo mejor de 3: 5,13 μs por bucle
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:
ab) 100000 bucles, lo mejor de 3: 7.05 μs por bucle
ba) 100000 bucles, lo mejor de 3: 2.4 μs por bucle
Con la entrada más larga:
ab) 100000 bucles, lo mejor de 3: 7.71 μs por bucle
ba) 100000 bucles, lo mejor de 3: 6.08 μs por bucle
Así que voy a usar bapara facilitar la lectura y la velocidad.
Apéndice
Impulsado por los hacks en los comentarios, una diferencia entre aby baes 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_checkestá 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!
@haccks No es necesario, pero es 2-3 veces más rápido. Cadena corta, con: 1.45 usec per loopy sin: 5.3 usec per loop, larga cadena, con: 4.38 usec per loopy 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.)
Hugo
1
@Hugo; Creo que esta diferencia en el tiempo se debe a que replacesolo se llama cuando cse encuentra texten caso de baque se llame en cada iteración ab.
piratea el
2
@haccks Gracias, he actualizado mi respuesta con más tiempos: agregar el cheque es mejor para ambos, ¡pero la lección más importante es que Python 3 es hasta 3 veces más rápido!
Hugo
73
>>> string="abc&def#ghi">>>for ch in['&','#']:...if ch in string:... string=string.replace(ch,"\\"+ch)...>>>print string
abc\&def\#ghi
¿Por qué se necesitaba una barra invertida doble? ¿Por qué no solo "\" funciona?
axolotl
3
La barra invertida doble escapa a la barra invertida; de lo contrario, Python interpretaría "\" como un carácter de cita literal dentro de una cadena aún abierta.
Riet
¿Por qué lo necesitas string=string.replace(ch,"\\"+ch)? ¿No es string.replace(ch,"\\"+ch)suficiente?
MattSom
1
@MattSom replace () no modifica la cadena original, pero devuelve una copia. Entonces necesita la asignación para que el código tenga algún efecto.
Ben Brian
3
¿Realmente necesitas el if? Parece una duplicación de lo que hará el reemplazo de todos modos.
lorenzo
32
Simplemente encadene las replacefunciones como esta
Tarde a la fiesta, pero perdí mucho tiempo con este problema hasta que encontré mi respuesta.
Corto y dulce, translatees superior areplace . Si está más interesado en la optimización de la funcionalidad con el tiempo, no usereplace .
Úselo también translatesi no sabe si el conjunto de caracteres que se reemplazará se superpone al conjunto de caracteres utilizado para reemplazar.
Caso en punto:
Usarlo replaceingenuamente 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):returnlambda 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 usedef 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.
>>> a ='&#'>>>print a.replace('&', r'\&')
\&#>>>print a.replace('#', r'\#')&\#
>>>
Desea usar una cadena 'sin procesar' (denotada por el prefijo 'r' de la cadena de reemplazo), ya que las cadenas sin procesar no tratan especialmente la barra invertida.
Respuestas:
Reemplazar dos personajes
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:
Programado así:
Reemplazando 17 caracteres
Aquí hay un código similar para hacer lo mismo pero con más caracteres para escapar (\ `* _ {}> # + -.! $):
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:
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.Apéndice
Impulsado por los hacks en los comentarios, una diferencia entre
ab
yba
es elif c in text:
cheque. Probémoslos contra dos variantes más: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.
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, peroba
(con verificación) tiene una mayor ventaja en Python 2Sin 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!
fuente
if c in text:
necesario enba
?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 cuandoc
se encuentratext
en caso deba
que se llame en cada iteraciónab
.fuente
string=string.replace(ch,"\\"+ch)
? ¿No esstring.replace(ch,"\\"+ch)
suficiente?Simplemente encadene las
replace
funciones como estaSi los reemplazos van a ser más numerosos, puede hacerlo de esta manera genérica
fuente
Aquí hay un método python3 que usa
str.translate
ystr.maketrans
:La cadena impresa es
abc\&def\#ghi
.fuente
.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 serr'\#'
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
Puede que no sea el método más eficiente, pero creo que es el más fácil.
fuente
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.
fuente
Puede considerar escribir una función de escape genérica:
De esta manera, puede configurar su función con una lista de caracteres que se deben escapar.
fuente
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 ).
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.
fuente
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.
En python2.7 no tiene que importar reduce, pero en python3. * Debe importarlo desde el módulo functools.
fuente
Tal vez un bucle simple para reemplazar los caracteres:
fuente
¿Qué tal esto?
luego
salida
similar a la respuesta
fuente
Desea usar una cadena 'sin procesar' (denotada por el prefijo 'r' de la cadena de reemplazo), ya que las cadenas sin procesar no tratan especialmente la barra invertida.
fuente