Je dois remplacer certains caractères comme suit: &
➔ \&
, #
➔\#
, ...
J'ai codé comme suit, mais je suppose qu'il devrait y avoir un meilleur moyen. Des indices?
strs = strs.replace('&', '\&')
strs = strs.replace('#', '\#')
...
Je dois remplacer certains caractères comme suit: &
➔ \&
, #
➔\#
, ...
J'ai codé comme suit, mais je suppose qu'il devrait y avoir un meilleur moyen. Des indices?
strs = strs.replace('&', '\&')
strs = strs.replace('#', '\#')
...
Réponses:
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:
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'\#')
Chronométré comme ceci:
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')"
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):
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'\$')
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:
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:
Avec l'entrée la plus longue:
Je vais donc utiliser ba
pour la lisibilité et la vitesse.
Invité par haccks dans les commentaires, une différence entre ab
et ba
est 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_check
est 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!
if c in text:
nécessaire dans ba
?
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 lorsque c
se trouve text
dans le cas de ba
pendant qu'elle est appelée à chaque itération 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)
? N'est-ce pas juste string.replace(ch,"\\"+ch)
assez?
Enchaînez simplement les replace
fonctions comme celle-ci
strs = "abc&def#ghi"
print strs.replace('&', '\&').replace('#', '\#')
# abc\&def\#ghi
Si les remplacements vont être plus nombreux, vous pouvez le faire de cette manière générique
strs, replacements = "abc&def#ghi", {"&": "\&", "#": "\#"}
print "".join([replacements.get(c, c) for c in strs])
# abc\&def\#ghi
Voici une méthode python3 utilisant str.translate
et str.maketrans
:
s = "abc&def#ghi"
print(s.translate(str.maketrans({'&': '\&', '#': '\#'})))
La chaîne imprimée est abc\&def\#ghi
.
.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 être r'\#'
ou '\\#'
? Peut-être un problème de formatage du bloc de code.
Allez-vous toujours ajouter une barre oblique inverse? Si oui, essayez
import re
rx = re.compile('([&#])')
# ^^ fill in the characters here.
strs = rx.sub('\\\\\\1', strs)
Ce n'est peut-être pas la méthode la plus efficace mais je pense que c'est la plus simple.
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.
Vous pouvez envisager d'écrire une fonction d'échappement générique:
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 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 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 vous n'avez pas à importer de réduire mais en python3. * Vous devez l'importer depuis le module functools.
Peut-être une simple boucle pour remplacer les caractères:
a = '&#'
to_replace = ['&', '#']
for char in to_replace:
a = a.replace(char, "\\"+char)
print(a)
>>> \&\#