Comment remplacer plusieurs sous-chaînes d'une chaîne?


284

Je voudrais utiliser la fonction .replace pour remplacer plusieurs chaînes.

J'ai actuellement

string.replace("condition1", "")

mais aimerait avoir quelque chose comme

string.replace("condition1", "").replace("condition2", "text")

bien que cela ne ressemble pas à une bonne syntaxe

quelle est la bonne façon de procéder? un peu comme comment dans grep / regex vous pouvez faire \1et \2remplacer les champs de certaines chaînes de recherche


7
Avez-vous essayé toutes les solutions proposées? Lequel est le plus rapide?
tommy.carstensen

J'ai pris le temps de tester toutes les réponses dans différents scénarios. Voir stackoverflow.com/questions/59072514/…
Pablo

1
Honnêtement, je préfère votre approche enchaînée à toutes les autres. J'ai atterri ici en cherchant une solution et j'ai utilisé la vôtre et cela fonctionne très bien.
frakman1

@ frakman1 +1. aucune idée pourquoi ce n'est pas plus voté. Toutes les autres méthodes rendent le code beaucoup plus difficile à lire. S'il y avait un tableau de passe de fonction à remplacer, cela fonctionnerait. Mais votre méthode chaînée est la plus claire (au moins avec un nombre statique de remplacements)
IceFire

Réponses:


269

Voici un court exemple qui devrait faire l'affaire avec des expressions régulières:

import re

rep = {"condition1": "", "condition2": "text"} # define desired replacements here

# use these three lines to do the replacement
rep = dict((re.escape(k), v) for k, v in rep.iteritems()) 
#Python 3 renamed dict.iteritems to dict.items so use rep.items() for latest versions
pattern = re.compile("|".join(rep.keys()))
text = pattern.sub(lambda m: rep[re.escape(m.group(0))], text)

Par exemple:

>>> pattern.sub(lambda m: rep[re.escape(m.group(0))], "(condition1) and --condition2--")
'() and --text--'

7
Le remplacement se fait en un seul passage.
Andrew Clark

26
dkamins: ce n'est pas trop intelligent, ce n'est même pas aussi intelligent qu'il devrait l'être (nous devons regex-échapper les clés avant de les joindre avec "|"). pourquoi n'est-ce pas trop ingénieux? parce que de cette façon que nous le faisons en un seul passage (= rapide), et nous faisons tous les remplacements en même temps, ce qui évite les affrontements comme "spamham sha".replace("spam", "eggs").replace("sha","md5")étant au "eggmd5m md5"lieu de"eggsham md5"
mouton volant

8
@AndrewClark J'apprécierais grandement si vous pouviez expliquer ce qui se passe sur la dernière ligne avec lambda.
minéraux

11
Bonjour, j'ai créé un petit résumé avec une version plus claire de cet extrait. Il devrait également être légèrement plus efficace: gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
bgusach

15
Pour python 3, utilisez items () au lieu de iteritems ().
Jangari

127

Vous pourriez juste faire une jolie petite fonction de bouclage.

def replace_all(text, dic):
    for i, j in dic.iteritems():
        text = text.replace(i, j)
    return text

textest la chaîne complète et dicest un dictionnaire - chaque définition est une chaîne qui remplacera une correspondance avec le terme.

Remarque : en Python 3, iteritems()a été remplacé paritems()


Attention: les dictionnaires Python n'ont pas d'ordre d'itération fiable. Cette solution ne résout votre problème que si:

  • l'ordre des remplacements n'est pas pertinent
  • c'est correct pour un remplaçant de changer les résultats des remplacements précédents

Par exemple:

d = { "cat": "dog", "dog": "pig"}
my_sentence = "This is my cat and this is my dog."
replace_all(my_sentence, d)
print(my_sentence)

Sortie possible n ° 1:

"Voici mon cochon et voici mon cochon."

Sortie possible # 2

"Voici mon chien et voici mon cochon."

Une solution possible consiste à utiliser un OrderedDict.

from collections import OrderedDict
def replace_all(text, dic):
    for i, j in dic.items():
        text = text.replace(i, j)
    return text
od = OrderedDict([("cat", "dog"), ("dog", "pig")])
my_sentence = "This is my cat and this is my dog."
replace_all(my_sentence, od)
print(my_sentence)

Production:

"This is my pig and this is my pig."

Attention # 2: inefficace si votre textchaîne est trop grande ou s'il y a beaucoup de paires dans le dictionnaire.


37
L'ordre dans lequel vous appliquez les différents remplacements importera - donc au lieu d'utiliser un dict standard, envisagez d'utiliser un OrderedDict- ou une liste de 2 tuples.
slothrop

5
Cela rend l'itération de la chaîne deux fois ... pas bon pour les performances.
Valentin Lorentz

6
En termes de performances, c'est pire que ce que Valentin dit - il traversera le texte autant de fois qu'il y a d'éléments dans dic! Très bien si le «texte» est petit mais, terrible pour le gros texte.
JDonner

3
C'est une bonne solution pour certains cas. Par exemple, je veux juste sous 2 caractères et je me fiche de l'ordre dans lequel ils vont parce que les clés de substitution ne correspondent à aucune valeur. Mais je veux que ce soit clair.
Nathan Garabedian

5
Notez que cela peut donner des résultats inattendus car le texte nouvellement inséré dans la première itération peut être mis en correspondance dans la deuxième itération. Par exemple, si nous essayons naïvement de remplacer tous les «A» par «B» et tous les «B» par «C», la chaîne «AB» serait transformée en «CC», et non «BC».
Ambroz Bizjak

106

Pourquoi pas une solution comme celle-ci?

s = "The quick brown fox jumps over the lazy dog"
for r in (("brown", "red"), ("lazy", "quick")):
    s = s.replace(*r)

#output will be:  The quick red fox jumps over the quick dog

2
C'est super utile, simple et portable.
Déchiquetez

Semblait bien, mais ne remplaçait pas l'expression régulière comme dans: pour r dans ((r '\ s.', '.'), (R '\ s,', ',')):
Martin

2
pour le rendre à 1 ligne: ss = [s.replace (* r) pour r in (("brown", "red"), ("lazy", "quick"))] [0]
Mark K

95

Voici une variante de la première solution utilisant réduire, au cas où vous aimeriez être fonctionnel. :)

repls = {'hello' : 'goodbye', 'world' : 'earth'}
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls.iteritems(), s)

la version encore meilleure de martineau:

repls = ('hello', 'goodbye'), ('world', 'earth')
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls, s)

8
Serait plus simple de faire replsune séquence de tuples et de supprimer l' iteritems()appel. c'est à dire repls = ('hello', 'goodbye'), ('world', 'earth')et reduce(lambda a, kv: a.replace(*kv), repls, s). Fonctionnerait également inchangé en Python 3.
martineau

agréable! si vous utilisez python3, utilisez des éléments au lieu d'itérations (désormais supprimés dans les trucs dict).
e.arbitrio

2
@martineau: Ce n'est pas vrai que cela fonctionne inchangé en python3 depuis qu'il reducea été supprimé .
normanius

5
@normanius: reduceexiste toujours, mais il a été intégré au functoolsmodule (voir les docs ) en Python 3, donc quand j'ai dit inchangé, je voulais dire que le même code pouvait être exécuté - bien qu'il soit vrai qu'il faudrait qu'il reduceait été importédité si nécessaire car ce n'est plus un intégré.
martineau

35

Ceci est juste un récapitulatif plus concis des bonnes réponses FJ et MiniQuark. Tout ce dont vous avez besoin pour réaliser plusieurs remplacements de chaînes simultanés est la fonction suivante:

def multiple_replace(string, rep_dict):
    pattern = re.compile("|".join([re.escape(k) for k in sorted(rep_dict,key=len,reverse=True)]), flags=re.DOTALL)
    return pattern.sub(lambda x: rep_dict[x.group(0)], string)

Usage:

>>>multiple_replace("Do you like cafe? No, I prefer tea.", {'cafe':'tea', 'tea':'cafe', 'like':'prefer'})
'Do you prefer tea? No, I prefer cafe.'

Si vous le souhaitez, vous pouvez créer vos propres fonctions de remplacement dédiées à partir de celle-ci plus simple.


1
Bien que ce soit une bonne solution, les remplacements de chaînes simultanés ne donneront pas exactement les mêmes résultats que leur exécution séquentielle (chaînage) - bien que cela puisse ne pas avoir d'importance.
martineau

2
Bien sûr, avec rep_dict = {"but": "mut", "mutton": "lamb"}la chaîne "button"résulte "mutton"avec votre code, mais donnerait "lamb"si les remplacements étaient enchaînés, l'un après l'autre.
martineau

2
C'est la principale caractéristique de ce code, pas un défaut. Avec des remplacements enchaînés, il ne pouvait pas atteindre le comportement souhaité de substitution de deux mots simultanément et réciproquement comme dans mon exemple.
mmj

1
Cela pourrait ne pas sembler une grande fonctionnalité si vous n'en avez pas besoin. Mais ici on parle de remplacements simultanés , alors c'est bien la caractéristique principale. Avec des remplacements "chaînés", la sortie de l'exemple serait Do you prefer cafe? No, I prefer cafe., ce qui n'est pas du tout souhaitable .
mmj

@David écrivez votre propre réponse, votre modification est trop radicale
UmNyobe

29

J'ai construit cela sur l'excellente réponse des FJ:

import re

def multiple_replacer(*key_values):
    replace_dict = dict(key_values)
    replacement_function = lambda match: replace_dict[match.group(0)]
    pattern = re.compile("|".join([re.escape(k) for k, v in key_values]), re.M)
    return lambda string: pattern.sub(replacement_function, string)

def multiple_replace(string, *key_values):
    return multiple_replacer(*key_values)(string)

Utilisation unique:

>>> replacements = (u"café", u"tea"), (u"tea", u"café"), (u"like", u"love")
>>> print multiple_replace(u"Do you like café? No, I prefer tea.", *replacements)
Do you love tea? No, I prefer café.

Notez que le remplacement étant effectué en un seul passage, "café" devient "thé", mais ne revient pas à "café".

Si vous devez effectuer le même remplacement plusieurs fois, vous pouvez facilement créer une fonction de remplacement:

>>> my_escaper = multiple_replacer(('"','\\"'), ('\t', '\\t'))
>>> many_many_strings = (u'This text will be escaped by "my_escaper"',
                       u'Does this work?\tYes it does',
                       u'And can we span\nmultiple lines?\t"Yes\twe\tcan!"')
>>> for line in many_many_strings:
...     print my_escaper(line)
... 
This text will be escaped by \"my_escaper\"
Does this work?\tYes it does
And can we span
multiple lines?\t\"Yes\twe\tcan!\"

Améliorations:

  • transformé le code en fonction
  • support multiligne ajouté
  • correction d'un bug lors de l'échappement
  • facile à créer une fonction pour un remplacement multiple spécifique

Prendre plaisir! :-)


1
Quelqu'un pourrait-il expliquer cette étape par étape pour les noobs en python comme moi?
Julian Suarez

Compagnon noob en python ici, donc je vais essayer de le comprendre de manière incomplète .. a. décomposer les valeurs de clés en éléments à remplacer (clés jointes par "|") et logique (si la correspondance est une clé, renvoyer une valeur) b. faire un analyseur d'expressions rationnelles ("modèle" qui recherche des clés et utilise une logique donnée) - envelopper cela dans une fonction lambda et retourner. Choses que je regarde maintenant: re.M, et la nécessité de lambda pour la logique de remplacement.
Fox

1
@ Fox Vous l'avez compris. Vous pouvez définir une fonction au lieu d'utiliser un lambda, c'est juste pour raccourcir le code. Mais notez que pattern.subla fonction attend un seul paramètre (le texte à remplacer), la fonction doit donc y avoir accès replace_dict. re.Mpermet les remplacements multilignes (il est bien expliqué dans le doc: docs.python.org/2/library/re.html#re.M ).
MiniQuark

22

Je voudrais proposer l'utilisation de modèles de chaînes. Placez simplement la chaîne à remplacer dans un dictionnaire et tout est réglé! Exemple tiré de docs.python.org

>>> from string import Template
>>> s = Template('$who likes $what')
>>> s.substitute(who='tim', what='kung pao')
'tim likes kung pao'
>>> d = dict(who='tim')
>>> Template('Give $who $100').substitute(d)
Traceback (most recent call last):
[...]
ValueError: Invalid placeholder in string: line 1, col 10
>>> Template('$who likes $what').substitute(d)
Traceback (most recent call last):
[...]
KeyError: 'what'
>>> Template('$who likes $what').safe_substitute(d)
'tim likes $what'

Semble bien, mais lorsque l'ajout d'une clé non fournie dans substitutedéclenche une exception, soyez donc prudent lorsque vous obtenez des modèles des utilisateurs.
Bart Friederichs

2
Un inconvénient de cette approche est que le modèle doit contenir toutes, et pas plus que toutes, les chaînes $ à remplacer, voir ici
RolfBly

17

Dans mon cas, j'avais besoin d'un simple remplacement de clés uniques par des noms, alors j'ai pensé à ça:

a = 'This is a test string.'
b = {'i': 'I', 's': 'S'}
for x,y in b.items():
    a = a.replace(x, y)
>>> a
'ThIS IS a teSt StrIng.'

3
Cela fonctionne tant que vous n'avez pas de conflit de remplacement. Si vous remplaçiez ipar svous obtiendriez un comportement étrange.
bgusach

1
Si l'ordre est significatif, au lieu du dict ci-dessus, vous pouvez utiliser un tableau: b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) Alors si vous faites attention à ordonner vos paires de tableaux, vous pouvez vous assurer de ne pas remplacer () récursivement.
CODE-REaD

Il semble que les dict maintiennent maintenant l'ordre , à partir de Python 3.7.0. Je l'ai testé et cela fonctionne dans l'ordre sur ma machine avec la dernière version stable de Python 3.
James Koss

15

A partir du lancement Python 3.8et de l'introduction des expressions d'affectation (PEP 572) ( :=opérateur), nous pouvons appliquer les remplacements dans une liste de compréhension:

# text = "The quick brown fox jumps over the lazy dog"
# replacements = [("brown", "red"), ("lazy", "quick")]
[text := text.replace(a, b) for a, b in replacements]
# text = 'The quick red fox jumps over the quick dog'

Savez-vous si c'est plus efficace que d'utiliser replace dans une boucle? Je teste toutes les réponses pour les performances mais je n'ai pas encore 3.8.
Pablo

Pourquoi est-ce que j'obtiens la sortie dans une liste?
johnrao07

1
@ johnrao07 Eh bien, une compréhension de liste construit une liste. Voilà pourquoi, dans ce cas, vous obtenez ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']. Mais l'expression d'affectation ( text := text.replace) crée également de manière itérative de nouvelles versions de texten la mutant. Après la compréhension de la liste, vous pouvez utiliser la textvariable qui contient le texte modifié.
Xavier Guihot

1
Si vous souhaitez renvoyer la nouvelle version de texten une ligne, vous pouvez également utiliser [text := text.replace(a, b) for a, b in replacements][-1](notez le [-1]), qui extrait le dernier élément de la liste de compréhension; c'est-à-dire la dernière version de text.
Xavier Guihot

13

Voici mon 0,02 $. Il est basé sur la réponse d'Andrew Clark, juste un peu plus clair, et il couvre également le cas où une chaîne à remplacer est une sous-chaîne d'une autre chaîne à remplacer (une chaîne plus longue gagne)

def multireplace(string, replacements):
    """
    Given a string and a replacement map, it returns the replaced string.

    :param str string: string to execute replacements on
    :param dict replacements: replacement dictionary {value to find: value to replace}
    :rtype: str

    """
    # Place longer ones first to keep shorter substrings from matching
    # where the longer ones should take place
    # For instance given the replacements {'ab': 'AB', 'abc': 'ABC'} against 
    # the string 'hey abc', it should produce 'hey ABC' and not 'hey ABc'
    substrs = sorted(replacements, key=len, reverse=True)

    # Create a big OR regex that matches any of the substrings to replace
    regexp = re.compile('|'.join(map(re.escape, substrs)))

    # For each match, look up the new string in the replacements
    return regexp.sub(lambda match: replacements[match.group(0)], string)

C'est dans ce sens , n'hésitez pas à le modifier si vous avez une proposition.


1
Cela aurait dû être la réponse acceptée à la place, car l'expression régulière est construite à partir de toutes les clés en les triant par ordre décroissant de longueur et en les joignant à la | opérateur d'alternance regex. Et le tri est nécessaire pour que le plus long de tous les choix possibles soit sélectionné s'il existe des alternatives.
Sachin S

Je suis d'accord que c'est la meilleure solution, grâce au tri. En dehors du tri, il est identique à ma réponse d'origine, j'ai donc emprunté le tri pour ma solution aussi, pour m'assurer que personne ne manquera une fonctionnalité aussi importante.
mmj

6

J'avais besoin d'une solution où les chaînes à remplacer peuvent être des expressions régulières, par exemple pour aider à normaliser un long texte en remplaçant plusieurs espaces blancs par un seul. S'appuyant sur une chaîne de réponses provenant d'autres personnes, notamment MiniQuark et mmj, voici ce que j'ai trouvé:

def multiple_replace(string, reps, re_flags = 0):
    """ Transforms string, replacing keys from re_str_dict with values.
    reps: dictionary, or list of key-value pairs (to enforce ordering;
          earlier items have higher priority).
          Keys are used as regular expressions.
    re_flags: interpretation of regular expressions, such as re.DOTALL
    """
    if isinstance(reps, dict):
        reps = reps.items()
    pattern = re.compile("|".join("(?P<_%d>%s)" % (i, re_str[0])
                                  for i, re_str in enumerate(reps)),
                         re_flags)
    return pattern.sub(lambda x: reps[int(x.lastgroup[1:])][1], string)

Cela fonctionne pour les exemples donnés dans d'autres réponses, par exemple:

>>> multiple_replace("(condition1) and --condition2--",
...                  {"condition1": "", "condition2": "text"})
'() and --text--'

>>> multiple_replace('hello, world', {'hello' : 'goodbye', 'world' : 'earth'})
'goodbye, earth'

>>> multiple_replace("Do you like cafe? No, I prefer tea.",
...                  {'cafe': 'tea', 'tea': 'cafe', 'like': 'prefer'})
'Do you prefer tea? No, I prefer cafe.'

L'essentiel pour moi est que vous pouvez également utiliser des expressions régulières, par exemple pour remplacer uniquement des mots entiers, ou pour normaliser un espace blanc:

>>> s = "I don't want to change this name:\n  Philip II of Spain"
>>> re_str_dict = {r'\bI\b': 'You', r'[\n\t ]+': ' '}
>>> multiple_replace(s, re_str_dict)
"You don't want to change this name: Philip II of Spain"

Si vous souhaitez utiliser les clés du dictionnaire comme des chaînes normales, vous pouvez les échapper avant d'appeler multiple_replace en utilisant par exemple cette fonction:

def escape_keys(d):
    """ transform dictionary d by applying re.escape to the keys """
    return dict((re.escape(k), v) for k, v in d.items())

>>> multiple_replace(s, escape_keys(re_str_dict))
"I don't want to change this name:\n  Philip II of Spain"

La fonction suivante peut aider à trouver des expressions régulières erronées parmi vos clés de dictionnaire (car le message d'erreur de multiple_replace n'est pas très révélateur):

def check_re_list(re_list):
    """ Checks if each regular expression in list is well-formed. """
    for i, e in enumerate(re_list):
        try:
            re.compile(e)
        except (TypeError, re.error):
            print("Invalid regular expression string "
                  "at position {}: '{}'".format(i, e))

>>> check_re_list(re_str_dict.keys())

Notez qu'il ne chaîne pas les remplacements, mais les effectue simultanément. Cela le rend plus efficace sans contraindre ce qu'il peut faire. Pour imiter l'effet du chaînage, vous devrez peut-être simplement ajouter plus de paires de remplacement de chaîne et garantir l'ordre attendu des paires:

>>> multiple_replace("button", {"but": "mut", "mutton": "lamb"})
'mutton'
>>> multiple_replace("button", [("button", "lamb"),
...                             ("but", "mut"), ("mutton", "lamb")])
'lamb'

C'est bien, merci. Pourrait-il être amélioré pour permettre également l'utilisation de références arrières dans les substitutions? Je n'ai pas immédiatement compris comment ajouter cela.
cmarqu

La réponse à ma question ci-dessus est stackoverflow.com/questions/45630940/…
cmarqu

4

Voici un exemple plus efficace sur les longues chaînes avec de nombreux petits remplacements.

source = "Here is foo, it does moo!"

replacements = {
    'is': 'was', # replace 'is' with 'was'
    'does': 'did',
    '!': '?'
}

def replace(source, replacements):
    finder = re.compile("|".join(re.escape(k) for k in replacements.keys())) # matches every string we want replaced
    result = []
    pos = 0
    while True:
        match = finder.search(source, pos)
        if match:
            # cut off the part up until match
            result.append(source[pos : match.start()])
            # cut off the matched part and replace it in place
            result.append(replacements[source[match.start() : match.end()]])
            pos = match.end()
        else:
            # the rest after the last match
            result.append(source[pos:])
            break
    return "".join(result)

print replace(source, replacements)

Le but est d'éviter de nombreuses enchaînements de longues chaînes. Nous découpons la chaîne source en fragments, en remplaçant certains des fragments au fur et à mesure que nous formons la liste, puis joignons le tout en une chaîne.


2

Vous ne devriez vraiment pas le faire de cette façon, mais je trouve ça trop cool:

>>> replacements = {'cond1':'text1', 'cond2':'text2'}
>>> cmd = 'answer = s'
>>> for k,v in replacements.iteritems():
>>>     cmd += ".replace(%s, %s)" %(k,v)
>>> exec(cmd)

Maintenant, answer est le résultat de tous les remplacements à son tour

encore une fois, c'est très hacky et ce n'est pas quelque chose que vous devriez utiliser régulièrement. Mais c'est juste agréable de savoir que vous pouvez faire quelque chose comme ça si jamais vous en avez besoin.


2

Je me débattais aussi avec ce problème. Avec de nombreuses substitutions, les expressions régulières ont du mal et sont environ quatre fois plus lentes que le bouclage string.replace(dans mes conditions d'expérience).

Vous devez absolument essayer d'utiliser la bibliothèque Flashtext ( article de blog ici , Github ici ). Dans mon cas, c'était un peu plus de deux ordres de grandeur plus rapide, de 1,8 s à 0,015 s (les expressions régulières ont pris 7,7 s) pour chaque document.

Il est facile de trouver des exemples d'utilisation dans les liens ci-dessus, mais c'est un exemple de travail:

    from flashtext import KeywordProcessor
    self.processor = KeywordProcessor(case_sensitive=False)
    for k, v in self.my_dict.items():
        self.processor.add_keyword(k, v)
    new_string = self.processor.replace_keywords(string)

Notez que Flashtext effectue des substitutions en une seule passe (pour éviter que a -> b et b -> c ne traduisent «a» en «c»). Flashtext recherche également des mots entiers (donc «est» ne correspondra pas à «th is »). Cela fonctionne très bien si votre cible est de plusieurs mots (en remplaçant «C'est» par «Bonjour»).


Comment cela fonctionne-t-il si vous devez remplacer des balises HTML? Par exemple, remplacez <p>par /n. J'ai essayé votre approche mais avec des balises flashtext ne semble pas l'analyser?
alias51

1
Je ne sais pas pourquoi cela ne fonctionne pas comme prévu. Une possibilité est que ces balises ne soient pas séparées par des espaces et n'oubliez pas que Flashtext recherche des mots entiers. Un moyen de contourner cela consiste à utiliser un simple remplacement en premier, de sorte que "Hi <p> ​​there" devienne "Hi <p> ​​there". Vous devrez être prudent pour supprimer les espaces indésirables lorsque vous avez terminé (également remplacer simplement?). J'espère que cela pourra aider.
Pablo

Merci, pouvez-vous définir <et >marquer la fin d'un mot (mais être inclus dans le remplacement)?
alias51

1
Je crois que les "mots" ne sont marqués que par des espaces. Il existe peut-être des paramètres facultatifs que vous pouvez définir dans "KeywordProcessor". Sinon, considérez l'approche ci-dessus: remplacez "<" par "<", appliquez Flashtext puis substituez-le en arrière (dans votre cas, par exemple, "<" à "<" et "\ n" à "\ n" pourrait fonctionner).
Pablo

2

Je pense que cette question a besoin d'une réponse de fonction lambda récursive sur une seule ligne pour être complète, juste parce que. Donc là:

>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.popitem()), d)

Usage:

>>> mrep('abcabc', {'a': '1', 'c': '2'})
'1b21b2'

Remarques:

  • Cela consomme le dictionnaire d'entrée.
  • Les dictés Python conservent l'ordre des clés à partir de 3.6; les mises en garde correspondantes dans d'autres réponses ne sont plus pertinentes. Pour une compatibilité descendante, on pourrait recourir à une version basée sur les tuples:
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

Remarque: Comme pour toutes les fonctions récursives en python, une profondeur de récursivité trop grande (c'est-à-dire des dictionnaires de remplacement trop grands) entraînera une erreur. Voir par exemple ici .


Je rencontre RecursionError lorsque j'utilise un grand dictionnaire!
Pablo

@Pablo Intéressant. Large comment? Notez que cela se produit pour toutes les fonctions récursives. Voir par exemple ici: stackoverflow.com/questions/3323001/…
mcsoini

Mon dictionnaire de substitutions est proche de 100k termes ... jusqu'à présent, l'utilisation de string.replace est de loin la meilleure approche.
Pablo

1
@Pablo dans ce cas, vous ne pouvez pas utiliser de fonctions récursives. En général, sys.getrecursionlimit()c'est un couple de 1000, max. utilisez une boucle ou quelque chose comme ça, ou essayez de simplifier les substitutions.
mcsoini

Oui, j'ai bien peur qu'il n'y ait vraiment aucun raccourci ici.
Pablo

1

Je ne connais pas la vitesse mais c'est ma solution rapide de travail:

reduce(lambda a, b: a.replace(*b)
    , [('o','W'), ('t','X')] #iterable of pairs: (oldval, newval)
    , 'tomato' #The string from which to replace values
    )

... mais j'aime la réponse regex n ° 1 ci-dessus. Remarque - si une nouvelle valeur est une sous-chaîne d'une autre, l'opération n'est pas commutative.


1

Vous pouvez utiliser la pandasbibliothèque et la replacefonction qui prend en charge les correspondances exactes ainsi que les remplacements d'expressions régulières. Par exemple:

df = pd.DataFrame({'text': ['Billy is going to visit Rome in November', 'I was born in 10/10/2010', 'I will be there at 20:00']})

to_replace=['Billy','Rome','January|February|March|April|May|June|July|August|September|October|November|December', '\d{2}:\d{2}', '\d{2}/\d{2}/\d{4}']
replace_with=['name','city','month','time', 'date']

print(df.text.replace(to_replace, replace_with, regex=True))

Et le texte modifié est:

0    name is going to visit city in month
1                      I was born in date
2                 I will be there at time

Vous pouvez trouver un exemple ici . Notez que les remplacements sur le texte se font dans l'ordre où ils apparaissent dans les listes


1

Pour remplacer un seul caractère, utilisez les touches translateetstr.maketrans est ma méthode préférée.

tl; dr> result_string = your_string.translate(str.maketrans(dict_mapping))


démo

my_string = 'This is a test string.'
dict_mapping = {'i': 's', 's': 'S'}
result_good = my_string.translate(str.maketrans(dict_mapping))
result_bad = my_string
for x, y in dict_mapping.items():
    result_bad = result_bad.replace(x, y)
print(result_good)  # ThsS sS a teSt Strsng.
print(result_bad)   # ThSS SS a teSt StrSng.

0

À partir de la précieuse réponse d'Andrew, j'ai développé un script qui charge le dictionnaire à partir d'un fichier et élabore tous les fichiers du dossier ouvert pour effectuer les remplacements. Le script charge les mappages à partir d'un fichier externe dans lequel vous pouvez définir le séparateur. Je suis débutant mais j'ai trouvé ce script très utile lors de plusieurs substitutions dans plusieurs fichiers. Il a chargé un dictionnaire avec plus de 1000 entrées en quelques secondes. Ce n'est pas élégant mais ça a fonctionné pour moi

import glob
import re

mapfile = input("Enter map file name with extension eg. codifica.txt: ")
sep = input("Enter map file column separator eg. |: ")
mask = input("Enter search mask with extension eg. 2010*txt for all files to be processed: ")
suff = input("Enter suffix with extension eg. _NEW.txt for newly generated files: ")

rep = {} # creation of empy dictionary

with open(mapfile) as temprep: # loading of definitions in the dictionary using input file, separator is prompted
    for line in temprep:
        (key, val) = line.strip('\n').split(sep)
        rep[key] = val

for filename in glob.iglob(mask): # recursion on all the files with the mask prompted

    with open (filename, "r") as textfile: # load each file in the variable text
        text = textfile.read()

        # start replacement
        #rep = dict((re.escape(k), v) for k, v in rep.items()) commented to enable the use in the mapping of re reserved characters
        pattern = re.compile("|".join(rep.keys()))
        text = pattern.sub(lambda m: rep[m.group(0)], text)

        #write of te output files with the prompted suffice
        target = open(filename[:-4]+"_NEW.txt", "w")
        target.write(text)
        target.close()

0

c'est ma solution au problème. Je l'ai utilisé dans un chatbot pour remplacer les différents mots à la fois.

def mass_replace(text, dct):
    new_string = ""
    old_string = text
    while len(old_string) > 0:
        s = ""
        sk = ""
        for k in dct.keys():
            if old_string.startswith(k):
                s = dct[k]
                sk = k
        if s:
            new_string+=s
            old_string = old_string[len(sk):]
        else:
            new_string+=old_string[0]
            old_string = old_string[1:]
    return new_string

print mass_replace("The dog hunts the cat", {"dog":"cat", "cat":"dog"})

cela deviendra The cat hunts the dog


0

Un autre exemple: liste d'entrée

error_list = ['[br]', '[ex]', 'Something']
words = ['how', 'much[ex]', 'is[br]', 'the', 'fish[br]', 'noSomething', 'really']

La sortie souhaitée serait

words = ['how', 'much', 'is', 'the', 'fish', 'no', 'really']

Code:

[n[0][0] if len(n[0]) else n[1] for n in [[[w.replace(e,"") for e in error_list if e in w],w] for w in words]] 

-2

Ou tout simplement pour un hack rapide:

for line in to_read:
    read_buffer = line              
    stripped_buffer1 = read_buffer.replace("term1", " ")
    stripped_buffer2 = stripped_buffer1.replace("term2", " ")
    write_to_file = to_write.write(stripped_buffer2)

-2

Voici une autre façon de le faire avec un dictionnaire:

listA="The cat jumped over the house".split()
modify = {word:word for number,word in enumerate(listA)}
modify["cat"],modify["jumped"]="dog","walked"
print " ".join(modify[x] for x in listA)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.