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('#', '#')
...
101
demandé sur Hugo 2010-08-05 08:34:16

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, mais ba (à vérifier) a plus de plomb sur Python 2

  • Cependant, 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!

237
répondu Hugo 2017-10-29 07:01:17
>>> string="abc&def#ghi"
>>> for ch in ['&','#']:
...   if ch in string:
...      string=string.replace(ch,"\\"+ch)
...
>>> print string
abc\&def\#ghi
65
répondu ghostdog74 2010-08-05 05:54:54

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
19
répondu thefourtheye 2014-05-09 11:46:12

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.

13
répondu kennytm 2010-08-05 04:39:07

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.

7
répondu tommy.carstensen 2018-02-10 02:59:49

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.

6
répondu Victor Olex 2011-02-16 05:17:34

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.

1
répondu parity3 2016-01-29 22:53:46
>>> 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.

0
répondu jonesy 2011-02-16 03:22:02

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.

0
répondu CasualCoder3 2018-01-29 12:08:42