J'ai chronométré toutes les méthodes dans les réponses actuelles avec une supplémentaire.
Avec une chaîne d'entrée de abc&def#ghiet le remplacement et -> \ & # et -> \ #, le meilleur moyen était de chaîner les remplacements comme ceci: text.replace('&', '\&').replace('#', '\#').
Timings pour chaque fonction:
a) 1000000 boucles, le meilleur de 3: 1,47 μs par boucle
b) 1000000 boucles, le meilleur de 3: 1,51 μs par boucle
c) 100000 boucles, le meilleur de 3: 12,3 μs par boucle
d) 100000 boucles, le meilleur de 3: 12 μs par boucle
e) 100000 boucles, le meilleur de 3: 3,27 μs par boucle
f) 1000000 boucles, le meilleur de 3: 0,817 μs par boucle
g) 100000 boucles, le meilleur de 3: 3,64 μs par boucle
h) 1000000 boucles, le meilleur de 3: 0,927 μs par boucle
i) 1000000 boucles, le meilleur de 3: 0,814 μs par boucle
Voici les fonctions:
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'\#')
Voici un code similaire pour faire de même mais avec plus de caractères à échapper (\ `* _ {}> # + -.! $):
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'\$')
Voici les résultats pour la même chaîne d'entrée abc&def#ghi:
a) 100000 boucles, le meilleur de 3: 6,72 μs par boucle
b) 100000 boucles, le meilleur de 3: 2,64 μs par boucle
c) 100000 boucles, le meilleur de 3: 11,9 μs par boucle
d) 100000 boucles, le meilleur de 3: 4,92 μs par boucle
e) 100000 boucles, le meilleur de 3: 2,96 μs par boucle
f) 100000 boucles, le meilleur de 3: 4,29 μs par boucle
g) 100000 boucles, le meilleur de 3: 4,68 μs par boucle
h) 100000 boucles, le meilleur de 3: 4,73 μs par boucle
i) 100000 boucles, le meilleur de 3: 4,24 μs par boucle
Et avec une chaîne d'entrée plus longue ( ## *Something* and [another] thing in a longer sentence with {more} things to replace$):
a) 100000 boucles, le meilleur de 3: 7,59 μs par boucle
b) 100000 boucles, le meilleur de 3: 6,54 μs par boucle
c) 100000 boucles, le meilleur de 3: 16,9 μs par boucle
d) 100000 boucles, le meilleur de 3: 7,29 μs par boucle
e) 100000 boucles, le meilleur de 3: 12,2 μs par boucle
f) 100000 boucles, le meilleur de 3: 5,38 μs par boucle
g) 10000 boucles, le meilleur de 3: 21,7 μs par boucle
h) 100000 boucles, le meilleur de 3: 5,7 μs par boucle
i) 100000 boucles, le meilleur de 3: 5,13 μs par boucle
Ajout de quelques 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)
Avec l'entrée la plus courte:
ab) 100000 boucles, le meilleur de 3: 7,05 μs par boucle
ba) 100000 boucles, le meilleur de 3: 2,4 μs par boucle
Avec l'entrée la plus longue:
ab) 100000 boucles, le meilleur de 3: 7,71 μs par boucle
ba) 100000 boucles, le meilleur de 3: 6,08 μs par boucle
Je vais donc utiliser bapour la lisibilité et la vitesse.
Addenda
Invité par haccks dans les commentaires, une différence entre abet baest le if c in text:chèque. Testons-les contre deux autres 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)
Les temps en μs par boucle sur Python 2.7.14 et 3.6.3, et sur une machine différente de l'ensemble précédent, ne peuvent donc pas être comparés directement.
╭────────────╥──────┬───────────────┬──────┬──────────────────╮│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│└────────────╨──────┴───────────────┴──────┴──────────────────┘
Nous pouvons conclure que:
Ceux avec le chèque sont jusqu'à 4x plus rapides que ceux sans le chèque
ab_with_checkest légèrement en tête sur Python 3, mais ba(avec vérification) a une plus grande avance sur Python 2
Cependant, la plus grande leçon ici est que Python 3 est jusqu'à 3 fois plus rapide que Python 2 ! Il n'y a pas de différence énorme entre le plus lent sur Python 3 et le plus rapide sur Python 2!
@haccks Ce n'est pas nécessaire, mais c'est 2-3x plus rapide avec. Chaîne courte, avec: 1.45 usec per loop, et sans: 5.3 usec per loop, chaîne longue, avec: 4.38 usec per loopet sans: 7.03 usec per loop. (Notez que ceux-ci ne sont pas directement comparables aux résultats ci-dessus, car il s'agit d'une machine différente, etc.)
Hugo
1
@Hugo; Je pense que cette différence de temps est due à replaceest appelée uniquement lorsque cse trouve textdans le cas de bapendant qu'elle est appelée à chaque itération ab.
haccks
2
@haccks Merci, j'ai mis à jour ma réponse avec d'autres timings: l'ajout de la vérification est meilleur pour les deux, mais la plus grande leçon est que Python 3 est jusqu'à 3 fois plus rapide!
Hugo
73
>>> string="abc&def#ghi">>>for ch in['&','#']:...if ch in string:... string=string.replace(ch,"\\"+ch)...>>>print string
abc\&def\#ghi
Pourquoi une double barre oblique inverse était-elle nécessaire? Pourquoi le "\" ne fonctionne-t-il pas seulement?
axolotl
3
La double barre oblique inverse échappe à la barre oblique inverse, sinon python interpréterait "\" comme un caractère de citation littéral dans une chaîne encore ouverte.
Riet
Pourquoi en avez-vous besoin string=string.replace(ch,"\\"+ch)? N'est-ce pas juste string.replace(ch,"\\"+ch)assez?
MattSom
1
@MattSom replace () ne modifie pas la chaîne d'origine, mais renvoie une copie. Vous avez donc besoin de l'affectation pour que le code ait un effet.
Ben Brian
3
Avez-vous vraiment besoin du si? Cela ressemble à une duplication de ce que le remplacement fera de toute façon.
lorenzo
32
Enchaînez simplement les replacefonctions comme celle-ci
Tard dans la soirée, mais j'ai perdu beaucoup de temps avec ce problème jusqu'à ce que je trouve ma réponse.
Court et doux, translateest supérieur àreplace . Si vous êtes plus intéressé par l'optimisation de la fonctionnalité au fil du temps, n'utilisez pasreplace .
Utilisez également translatesi vous ne savez pas si le jeu de caractères à remplacer chevauche le jeu de caractères utilisé pour remplacer.
Exemple concret:
Utiliser replacevous vous attendrait naïvement à ce que l'extrait "1234".replace("1", "2").replace("2", "3").replace("3", "4")revienne "2344", mais il reviendra en fait "4444".
La traduction semble effectuer ce que l'OP souhaitait à l'origine.
Vous pouvez envisager d'écrire une fonction d'échappement générique:
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 cette façon, vous pouvez rendre votre fonction configurable avec une liste de caractères à échapper.
Pour info, cela est peu ou pas utile à l'OP mais il peut être utile à d'autres lecteurs (veuillez ne pas voter contre, j'en suis conscient).
Comme un exercice quelque peu ridicule mais intéressant, je voulais voir si je pouvais utiliser la programmation fonctionnelle python pour remplacer plusieurs caractères. Je suis presque sûr que cela ne bat PAS simplement en appelant replace () deux fois. Et si les performances étaient un problème, vous pourriez facilement battre cela en rouille, C, julia, perl, java, javascript et peut-être même awk. Il utilise un package «helpers» externe appelé pytoolz , accéléré via cython ( cytoolz, c'est un package 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),))))))
Je ne vais même pas expliquer cela car personne ne prendrait la peine de l'utiliser pour effectuer plusieurs remplacements. Néanmoins, je me sentais quelque peu accompli en faisant cela et pensais que cela pourrait inspirer d'autres lecteurs ou gagner un concours d'obscurcissement de code.
En utilisant réduire qui est disponible en python2.7 et python3. *, Vous pouvez facilement remplacer plusieurs sous-chaînes de manière propre et pythonique.
# 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 vous n'avez pas à importer de réduire mais en python3. * Vous devez l'importer depuis le module functools.
>>> a ='&#'>>>print a.replace('&', r'\&')
\&#>>>print a.replace('#', r'\#')&\#
>>>
Vous souhaitez utiliser une chaîne «brute» (indiquée par le préfixe «r» de la chaîne de remplacement), car les chaînes brutes ne traitent pas spécialement la barre oblique inverse.
Réponses:
Remplacement de deux caractères
J'ai chronométré toutes les méthodes dans les réponses actuelles avec une supplémentaire.
Avec une chaîne d'entrée de
abc&def#ghi
et le remplacement et -> \ & # et -> \ #, le meilleur moyen était de chaîner les remplacements comme ceci:text.replace('&', '\&').replace('#', '\#')
.Timings pour chaque fonction:
Voici les fonctions:
Chronométré comme ceci:
Remplacement de 17 caractères
Voici un code similaire pour faire de même mais avec plus de caractères à échapper (\ `* _ {}> # + -.! $):
Voici les résultats pour la même chaîne d'entrée
abc&def#ghi
:Et avec une chaîne d'entrée plus longue (
## *Something* and [another] thing in a longer sentence with {more} things to replace$
):Ajout de quelques variantes:
Avec l'entrée la plus courte:
Avec l'entrée la plus longue:
Je vais donc utiliser
ba
pour la lisibilité et la vitesse.Addenda
Invité par haccks dans les commentaires, une différence entre
ab
etba
est leif c in text:
chèque. Testons-les contre deux autres variantes:Les temps en μs par boucle sur Python 2.7.14 et 3.6.3, et sur une machine différente de l'ensemble précédent, ne peuvent donc pas être comparés directement.
Nous pouvons conclure que:
Ceux avec le chèque sont jusqu'à 4x plus rapides que ceux sans le chèque
ab_with_check
est légèrement en tête sur Python 3, maisba
(avec vérification) a une plus grande avance sur Python 2Cependant, la plus grande leçon ici est que Python 3 est jusqu'à 3 fois plus rapide que Python 2 ! Il n'y a pas de différence énorme entre le plus lent sur Python 3 et le plus rapide sur Python 2!
la source
if c in text:
nécessaire dansba
?1.45 usec per loop
, et sans:5.3 usec per loop
, chaîne longue, avec:4.38 usec per loop
et sans:7.03 usec per loop
. (Notez que ceux-ci ne sont pas directement comparables aux résultats ci-dessus, car il s'agit d'une machine différente, etc.)replace
est appelée uniquement lorsquec
se trouvetext
dans le cas deba
pendant qu'elle est appelée à chaque itérationab
.la source
string=string.replace(ch,"\\"+ch)
? N'est-ce pas justestring.replace(ch,"\\"+ch)
assez?Enchaînez simplement les
replace
fonctions comme celle-ciSi les remplacements vont être plus nombreux, vous pouvez le faire de cette manière générique
la source
Voici une méthode python3 utilisant
str.translate
etstr.maketrans
:La chaîne imprimée est
abc\&def\#ghi
.la source
.translate()
semble plus lent que trois chaînés.replace()
(en utilisant CPython 3.6.4).replace()
moi-même, mais j'ai ajouté cette réponse par souci d'exhaustivité.'\#'
valide? ça ne devrait pas êtrer'\#'
ou'\\#'
? Peut-être un problème de formatage du bloc de code.Allez-vous toujours ajouter une barre oblique inverse? Si oui, essayez
Ce n'est peut-être pas la méthode la plus efficace mais je pense que c'est la plus simple.
la source
r'\\\1'
Tard dans la soirée, mais j'ai perdu beaucoup de temps avec ce problème jusqu'à ce que je trouve ma réponse.
Court et doux,
translate
est supérieur àreplace
. Si vous êtes plus intéressé par l'optimisation de la fonctionnalité au fil du temps, n'utilisez pasreplace
.Utilisez également
translate
si vous ne savez pas si le jeu de caractères à remplacer chevauche le jeu de caractères utilisé pour remplacer.Exemple concret:
Utiliser
replace
vous vous attendrait naïvement à ce que l'extrait"1234".replace("1", "2").replace("2", "3").replace("3", "4")
revienne"2344"
, mais il reviendra en fait"4444"
.La traduction semble effectuer ce que l'OP souhaitait à l'origine.
la source
Vous pouvez envisager d'écrire une fonction d'échappement générique:
De cette façon, vous pouvez rendre votre fonction configurable avec une liste de caractères à échapper.
la source
Pour info, cela est peu ou pas utile à l'OP mais il peut être utile à d'autres lecteurs (veuillez ne pas voter contre, j'en suis conscient).
Comme un exercice quelque peu ridicule mais intéressant, je voulais voir si je pouvais utiliser la programmation fonctionnelle python pour remplacer plusieurs caractères. Je suis presque sûr que cela ne bat PAS simplement en appelant replace () deux fois. Et si les performances étaient un problème, vous pourriez facilement battre cela en rouille, C, julia, perl, java, javascript et peut-être même awk. Il utilise un package «helpers» externe appelé pytoolz , accéléré via cython ( cytoolz, c'est un package pypi ).
Je ne vais même pas expliquer cela car personne ne prendrait la peine de l'utiliser pour effectuer plusieurs remplacements. Néanmoins, je me sentais quelque peu accompli en faisant cela et pensais que cela pourrait inspirer d'autres lecteurs ou gagner un concours d'obscurcissement de code.
la source
En utilisant réduire qui est disponible en python2.7 et python3. *, Vous pouvez facilement remplacer plusieurs sous-chaînes de manière propre et pythonique.
En python2.7 vous n'avez pas à importer de réduire mais en python3. * Vous devez l'importer depuis le module functools.
la source
Peut-être une simple boucle pour remplacer les caractères:
la source
Que dis-tu de ça?
puis
production
similaire à répondre
la source
Vous souhaitez utiliser une chaîne «brute» (indiquée par le préfixe «r» de la chaîne de remplacement), car les chaînes brutes ne traitent pas spécialement la barre oblique inverse.
la source