Cronometrei todos os métodos nas respostas atuais, juntamente com um extra.
Com uma cadeia de entrada de abc&def#ghie substituindo & -> \ & # e -> \ #, a maneira mais rápida foi para encadear as substituições como este: text.replace('&', '\&').replace('#', '\#').
Horários para cada função:
a) 1000000 loops, melhor de 3: 1,47 μs por loop
b) 1000000 loops, melhor de 3: 1,51 μs por loop
c) 100000 loops, melhor de 3: 12,3 μs por loop
d) 100000 loops, melhor de 3: 12 μs por loop
e) 100000 loops, melhor de 3: 3,27 μs por loop
f) 1000000 loops, melhor de 3: 0,817 μs por loop
g) 100000 loops, melhor de 3: 3,64 μs por loop
h) 1000000 loops, melhor de 3: 0,927 μs por loop
i) 1000000 loops, melhor de 3: 0,814 μs por loop
Aqui estão as funções:
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'\#')
Aqui está um código semelhante para fazer o mesmo, mas com mais 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'\$')
Aqui estão os resultados para a mesma sequência de entrada abc&def#ghi:
a) 100000 loops, melhor de 3: 6,72 μs por loop
b) 100000 loops, melhor de 3: 2,64 μs por loop
c) 100000 loops, melhor de 3: 11,9 μs por loop
d) 100000 loops, melhor de 3: 4,92 μs por loop
e) 100000 loops, melhor de 3: 2,96 μs por loop
f) 100000 loops, melhor de 3: 4,29 μs por loop
g) 100000 loops, o melhor de 3: 4,68 μs por loop
h) 100000 loops, melhor de 3: 4,73 μs por loop
i) 100000 loops, o melhor de 3: 4,24 μs por loop
E com uma string de entrada mais longa ( ## *Something* and [another] thing in a longer sentence with {more} things to replace$):
a) 100000 loops, o melhor de 3: 7,59 μs por loop
b) 100000 loops, melhor de 3: 6,54 μs por loop
c) 100000 loops, melhor de 3: 16,9 μs por loop
d) 100000 loops, melhor de 3: 7,29 μs por loop
e) 100000 loops, melhor de 3: 12,2 μs por loop
f) 100000 loops, melhor de 3: 5,38 μs por loop
g) 10000 loops, melhor de 3: 21,7 μs por loop
h) 100000 loops, o melhor de 3: 5,7 μs por loop
i) 100000 loops, o melhor de 3: 5,13 μs por loop
Adicionando algumas 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)
Com a entrada mais curta:
ab) 100000 loops, melhor de 3: 7,05 μs por loop
ba) 100000 loops, o melhor de 3: 2,4 μs por loop
Com a entrada mais longa:
ab) 100000 loops, melhor de 3: 7,71 μs por loop
ba) 100000 loops, o melhor de 3: 6,08 μs por loop
Então, eu vou usar bapara facilitar a leitura e a velocidade.
Termo aditivo
Solicitado por hackers nos comentários, uma diferença entre abe baé a if c in text:verificação. Vamos testá-los em mais duas variantes:
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)
Os tempos em µs por loop no Python 2.7.14 e 3.6.3 e em uma máquina diferente do conjunto anterior, portanto, não podem ser comparados diretamente.
╭────────────╥──────┬───────────────┬──────┬──────────────────╮│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│└────────────╨──────┴───────────────┴──────┴──────────────────┘
Nos podemos concluir que:
Aqueles com o cheque são até 4x mais rápidos do que aqueles sem o cheque
ab_with_checkestá ligeiramente à frente no Python 3, mas ba(com verificação) tem uma vantagem maior no Python 2
No entanto, a maior lição aqui é que o Python 3 é até 3x mais rápido que o Python 2 ! Não há uma grande diferença entre os mais lentos no Python 3 e os mais rápidos no Python 2!
@haccks Não é necessário, mas é 2-3 vezes mais rápido. Corda curta, com: 1.45 usec per loope sem: 5.3 usec per loop, cadeia longa, com: 4.38 usec per loope sem: 7.03 usec per loop. (Observe que estes não são diretamente comparáveis com os resultados acima, porque é uma máquina diferente etc.) #
Hugo
1
@Hugo; Eu acho que essa diferença no tempo é devido a que replaceé chamada apenas quando cé encontrada textno caso de baenquanto é chamada em todas as iterações em ab.
haccks
2
@haccks Obrigado, atualizei minha resposta com mais intervalos: adicionar a verificação é melhor para ambos, mas a maior lição é que o Python 3 é até 3x mais 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 que era necessária uma barra invertida dupla? Por que simplesmente "\" não funciona?
axolotl
3
A barra invertida dupla escapa da barra invertida; caso contrário, o python interpretaria "\" como um caractere de citação literal dentro de uma string ainda aberta.
Riet 21/07
Por que você precisa string=string.replace(ch,"\\"+ch)? Não é apenas o string.replace(ch,"\\"+ch)suficiente?
MattSom
1
@MattSom replace () não modifica a string original, mas retorna uma cópia. Portanto, você precisa que a atribuição do código tenha qualquer efeito.
Ben Brian
3
Você realmente precisa do if? Parece uma duplicação do que a substituição fará de qualquer maneira.
Essa é uma boa resposta, mas, na prática, a execução de uma .translate()parece ser mais lenta que a de três cadeias .replace()(usando o CPython 3.6.4).
Changaco
@Changaco Obrigado por cronometrar 👍 Na prática, eu replace()me usaria , mas adicionei esta resposta por uma questão de integridade.
Tommy.carstensen
Para grandes cordas e muitas substituições este deve ser mais rápido, embora alguns testes seria bom ...
Graipher
Bem, não está na minha máquina (o mesmo para 2 e 17 substituições).
Graipher
como é '\#'válido? não deveria ser r'\#'ou '\\#'? Talvez seja um problema de formatação do bloco de código.
parity3 10/05/19
16
Você sempre vai acrescentar uma barra invertida? Se sim, tente
import re
rx = re.compile('([&#])')# ^^ fill in the characters here.
strs = rx.sub('\\\\\\1', strs)
Pode não ser o método mais eficiente, mas acho que é o mais fácil.
Cheguei atrasado na festa, mas perdi muito tempo com esse problema até encontrar minha resposta.
Curto e doce, translateé superior areplace . Se você estiver mais interessado na otimização da funcionalidade ao longo do tempo, não use replace.
Use também translatese você não souber se o conjunto de caracteres a ser substituído se sobrepõe ao conjunto de caracteres usado para substituir.
Caso em questão:
Usando replacevocê, você esperaria ingenuamente que o snippet "1234".replace("1", "2").replace("2", "3").replace("3", "4")retornasse "2344", mas ele retornará de fato "4444".
A tradução parece executar o OP originalmente desejado.
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
Dessa forma, você pode configurar sua função com uma lista de caracteres que devem ser escapados.
Para sua informação, isso é de pouca ou nenhuma utilidade para o OP, mas pode ser útil para outros leitores (por favor, não diminua o voto, estou ciente disso).
Como um exercício um tanto ridículo, mas interessante, queria ver se eu poderia usar a programação funcional python para substituir vários caracteres. Eu tenho certeza que isso não bate apenas chamando replace () duas vezes. E se o desempenho fosse um problema, você poderia facilmente superar isso com ferrugem, C, julia, perl, java, javascript e talvez até awk. Ele usa um pacote de 'ajudantes' externo chamado pytoolz , acelerado via cython ( cytoolz, é um pacote 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),))))))
Eu nem vou explicar isso, porque ninguém se incomodaria em usar isso para realizar substituições múltiplas. No entanto, me senti um pouco realizado ao fazer isso e pensei que poderia inspirar outros leitores ou ganhar um concurso de ofuscação de código.
Usando o reduzir disponível em python2.7 e python3. *, Você pode substituir facilmente substrings múltiplos de maneira limpa e 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"
No python2.7 você não precisa importar o reduzir, mas no python3. * Você precisa importá-lo do módulo functools.
>>> a ='&#'>>>print a.replace('&', r'\&')
\&#>>>print a.replace('#', r'\#')&\#
>>>
Você deseja usar uma cadeia de caracteres 'bruta' (indicada pelo 'r' que prefixa a cadeia de substituição), pois as cadeias brutas não tratam a barra invertida especialmente.
Respostas:
Substituindo dois caracteres
Cronometrei todos os métodos nas respostas atuais, juntamente com um extra.
Com uma cadeia de entrada de
abc&def#ghi
e substituindo & -> \ & # e -> \ #, a maneira mais rápida foi para encadear as substituições como este:text.replace('&', '\&').replace('#', '\#')
.Horários para cada função:
Aqui estão as funções:
Cronometrado assim:
Substituindo 17 caracteres
Aqui está um código semelhante para fazer o mesmo, mas com mais caracteres para escapar (\ `* _ {}> # + -.! $):
Aqui estão os resultados para a mesma sequência de entrada
abc&def#ghi
:E com uma string de entrada mais longa (
## *Something* and [another] thing in a longer sentence with {more} things to replace$
):Adicionando algumas variantes:
Com a entrada mais curta:
Com a entrada mais longa:
Então, eu vou usar
ba
para facilitar a leitura e a velocidade.Termo aditivo
Solicitado por hackers nos comentários, uma diferença entre
ab
eba
é aif c in text:
verificação. Vamos testá-los em mais duas variantes:Os tempos em µs por loop no Python 2.7.14 e 3.6.3 e em uma máquina diferente do conjunto anterior, portanto, não podem ser comparados diretamente.
Nos podemos concluir que:
Aqueles com o cheque são até 4x mais rápidos do que aqueles sem o cheque
ab_with_check
está ligeiramente à frente no Python 3, masba
(com verificação) tem uma vantagem maior no Python 2No entanto, a maior lição aqui é que o Python 3 é até 3x mais rápido que o Python 2 ! Não há uma grande diferença entre os mais lentos no Python 3 e os mais rápidos no Python 2!
fonte
if c in text:
necessário entrarba
?1.45 usec per loop
e sem:5.3 usec per loop
, cadeia longa, com:4.38 usec per loop
e sem:7.03 usec per loop
. (Observe que estes não são diretamente comparáveis com os resultados acima, porque é uma máquina diferente etc.) #replace
é chamada apenas quandoc
é encontradatext
no caso deba
enquanto é chamada em todas as iterações emab
.fonte
string=string.replace(ch,"\\"+ch)
? Não é apenas ostring.replace(ch,"\\"+ch)
suficiente?Simplesmente encadeie
replace
funções como estaSe as substituições forem mais numerosas, você poderá fazer isso dessa maneira genérica
fonte
Aqui está um método python3 usando
str.translate
estr.maketrans
:A sequência impressa é
abc\&def\#ghi
.fonte
.translate()
parece ser mais lenta que a de três cadeias.replace()
(usando o CPython 3.6.4).replace()
me usaria , mas adicionei esta resposta por uma questão de integridade.'\#'
válido? não deveria serr'\#'
ou'\\#'
? Talvez seja um problema de formatação do bloco de código.Você sempre vai acrescentar uma barra invertida? Se sim, tente
Pode não ser o método mais eficiente, mas acho que é o mais fácil.
fonte
r'\\\1'
Cheguei atrasado na festa, mas perdi muito tempo com esse problema até encontrar minha resposta.
Curto e doce,
translate
é superior areplace
. Se você estiver mais interessado na otimização da funcionalidade ao longo do tempo, não usereplace
.Use também
translate
se você não souber se o conjunto de caracteres a ser substituído se sobrepõe ao conjunto de caracteres usado para substituir.Caso em questão:
Usando
replace
você, você esperaria ingenuamente que o snippet"1234".replace("1", "2").replace("2", "3").replace("3", "4")
retornasse"2344"
, mas ele retornará de fato"4444"
.A tradução parece executar o OP originalmente desejado.
fonte
Você pode escrever uma função de escape genérica:
Dessa forma, você pode configurar sua função com uma lista de caracteres que devem ser escapados.
fonte
Para sua informação, isso é de pouca ou nenhuma utilidade para o OP, mas pode ser útil para outros leitores (por favor, não diminua o voto, estou ciente disso).
Como um exercício um tanto ridículo, mas interessante, queria ver se eu poderia usar a programação funcional python para substituir vários caracteres. Eu tenho certeza que isso não bate apenas chamando replace () duas vezes. E se o desempenho fosse um problema, você poderia facilmente superar isso com ferrugem, C, julia, perl, java, javascript e talvez até awk. Ele usa um pacote de 'ajudantes' externo chamado pytoolz , acelerado via cython ( cytoolz, é um pacote pypi ).
Eu nem vou explicar isso, porque ninguém se incomodaria em usar isso para realizar substituições múltiplas. No entanto, me senti um pouco realizado ao fazer isso e pensei que poderia inspirar outros leitores ou ganhar um concurso de ofuscação de código.
fonte
Usando o reduzir disponível em python2.7 e python3. *, Você pode substituir facilmente substrings múltiplos de maneira limpa e pitônica.
No python2.7 você não precisa importar o reduzir, mas no python3. * Você precisa importá-lo do módulo functools.
fonte
Talvez um loop simples para caracteres substituir:
fonte
Que tal agora?
então
resultado
semelhante a responder
fonte
Você deseja usar uma cadeia de caracteres 'bruta' (indicada pelo 'r' que prefixa a cadeia de substituição), pois as cadeias brutas não tratam a barra invertida especialmente.
fonte