Remplacer plusieurs caractères par Python
J'ai besoin de remplacer certains caractères comme suit: &
-> &
, #
-> #
, ...
J'ai codé comme suit, mais je suppose qu'il devrait y avoir un meilleur moyen. Tous les conseils?
strs = strs.replace('&', '&')
strs = strs.replace('#', '#')
...
9 réponses
Remplacement de deux caractères
J'ai chronométré toutes les méthodes dans les réponses actuelles avec un extra.
Avec une chaîne d'entrée de abc&def#ghi
et en remplaçant & - > \ & et# -> #, le plus rapide était d'enchaîner les remplacements comme: text.replace('&', '\&').replace('#', '\#')
.
Horaires pour chaque fonction:
- a) 1000000 boucles, meilleur de 3: 1,47 µs par boucle
- B) 1000000 boucles, meilleur de 3: 1,51 µs par boucle
- c) 100000 boucles, meilleur de 3: 12.3 µs par boucle
- d) 100000 boucles, meilleur de 3: 12 µs par boucle
- e) 100000 boucles, meilleur de 3: 3.27 µs par boucle
- f) 1000000 boucles, meilleur de 3: 0.817 µs par boucle
- g) 100000 boucles, meilleur de 3: 3,64 µs par boucle
- h) 1000000 boucles, meilleur de 3: 0,927 µs par boucle
- i) 1000000 boucles, 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):
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')"
Remplacer 17 caractères
Voici un code similaire pour faire la même chose 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
:
- a) 100000 boucles, au meilleur de 3: 6,72 µs par boucle
- b) 100000 boucles, meilleur de 3: 2,64 µs par boucle
- c) 100000 boucles, au meilleur de 3: 11,9 µs par boucle
- d) 100000 boucles, meilleur de 3: 4.92 µs par boucle
- e) 100000 boucles, meilleur de 3: 2.96 µs par boucle
- f) 100000 boucles, meilleur de 3: 4,29 µs par boucle
- g) 100000 boucles, au meilleur de 3: 4,68 µs par boucle
- h) 100000 boucles, le meilleur de 3: 4,73 µs par boucle
- i) 100000 boucles, 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, au meilleur de 3: 6,54 µs par boucle
- c) 100000 boucles, le meilleur de 3: 16.9 µs par boucle
- d) 100000 boucles, meilleur de 3: 7.29 µs par boucle
- e) 100000 boucles, le meilleur de 3: 12.2 µs par boucle
- f) 100000 boucles, meilleur de 3: 5,38 µs par boucle
- g) 10000 boucles, meilleur de 3: 21.7 µs par boucle
- h) 100000 boucles, meilleur de 3: 5,7 µs par boucle
- i) 100000 boucles, 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, meilleur de 3: 7.05 µs par boucle
- ba) 100000 boucles, au meilleur de 3: 2,4 µs par boucle
Avec l'entrée la plus longue:
- ab) 100000 boucles, meilleur de 3: 7.71 µs par boucle
- ba) 100000 boucles, le meilleur de 3: 6,08 µs par boucle
Donc je vais utiliser ba
pour la lisibilité et la vitesse.
Additif
Invité par haccks dans les commentaires, une différence entre ab
et ba
est le if c in text:
vérifier. 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)
Fois 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, donc ne peut pas être comparé 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'à 4 fois plus rapides que ceux sans la vérifier
ab_with_check
est légèrement en avance sur Python 3, maisba
(à vérifier) a plus de plomb sur Python 2Cependant, la plus grande leçon ici est Python 3 est jusqu'à 3 fois plus rapide que Python 2 ! Il n'y a pas une énorme différence entre le plus lent sur Python 3 et le plus rapide sur Python 2!
>>> string="abc&def#ghi"
>>> for ch in ['&','#']:
... if ch in string:
... string=string.replace(ch,"\\"+ch)
...
>>> print string
abc\&def\#ghi
Enchaînez simplement les fonctions replace
comme ceci
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
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 facile.
Voici une méthode à l'aide de python3 str.translate
et str.maketrans
:
s = "abc&def#ghi"
print(s.translate(str.maketrans({'&': '\&', '#': '\#'})))
La chaîne imprimée est abc\&def\#ghi
.
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 qui doivent être échappés.
POUR INFO, ceci est peu ou pas utile à L'OP mais il peut être utile à d'autres lecteurs (veuillez ne pas downvote, j'en suis conscient).
Comme un exercice un peu ridicule mais intéressant, je voulais voir si je pouvais utiliser la programmation fonctionnelle python pour remplacer plusieurs caractères. Je suis sûr que cela ne bat pas juste appeler replace () deux fois. Et si la performance était un problème, vous pourriez facilement battre cela dans rust, C, julia, perl, java, javascript et peut-être même awk. Il utilise un externe "aidants" paquet appelé pytoolz , accéléré via cython ( cytoolz, c'est un paquet 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 parce que personne ne prendrait la peine d'utiliser cela pour accomplir plusieurs remplacements. Néanmoins, je me suis senti quelque peu accompli en faisant cela et j'ai pensé que cela pourrait inspirer d'autres lecteurs ou gagner un concours d'obscurcissement de code.
>>> a = '&#'
>>> print a.replace('&', r'\&')
\&#
>>> print a.replace('#', r'\#')
&\#
>>>
Vous voulez utiliser une chaîne' raw '(désignée par le' r ' préfixant la chaîne de remplacement), puisque les chaînes raw ne traitent pas spécialement la barre oblique inverse.
En utilisant reduce qui est disponible en python2. 7 et python3.* vous pouvez facilement remplacer mutiple sous-chaînes d'une 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 besoin d'importer reduce mais en python3.* vous devez l'importer depuis le module functools.