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 semble pas être une bonne syntaxe
quelle est la bonne façon de faire cela? un peu comme dans grep/regex vous pouvez utiliser \1
et \2
pour remplacer les champs de certaines chaînes de recherche
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--'
Vous pouvez juste faire une jolie petite fonction en boucle.
def replace_all(text, dic):
for i, j in dic.iteritems():
text = text.replace(i, j)
return text
où text
est la chaîne complète et dic
est un dictionnaire - chaque définition est une chaîne qui remplacera une correspondance avec le terme.
Note: dans Python 3, iteritems()
a été remplacé par items()
Attention: Python les dictionnaires n'ont pas d'ordre fiable pour les itérations. Cette solution ne résout votre problème que si:
Par exemple:
d = { "cat": "dog", "dog": "pig"}
mySentence = "This is my cat and this is my dog."
replace_all(mySentence, d)
print(mySentence)
Sortie possible n ° 1:
"Ceci est mon cochon et c'est mon cochon."
Sortie possible n ° 2
"Ceci est mon chien et ceci est 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")])
mySentence = "This is my cat and this is my dog."
replace_all(mySentence, od)
print(mySentence)
Sortie:
"This is my pig and this is my pig."
Attention # 2: Inefficace si votre chaîne text
est trop grosse ou s'il y a beaucoup de paires dans le dictionnaire.
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)
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
Ceci est juste une récapitulation plus concise des excellentes réponses de F.J et MiniQuark. Tout ce dont vous avez besoin pour obtenir plusieurs remplacements de chaîne 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 cette fonction plus simple.
J'ai construit ceci sur l'excellente réponse de F.J.s:
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)
Un coup d'utilisation:
>>> 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 puisque le remplacement est effectué en un seul passage, "café" devient "thé" mais ne redevient pas "café".
Si vous devez effectuer plusieurs fois le même remplacement, 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:
Enjoy!: -)
Je voudrais proposer l'utilisation de modèles de chaîne. Il suffit de placer la chaîne à remplacer dans un dictionnaire et tout est défini! Exemple 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'
Dans mon cas, j'avais besoin d'un simple remplacement de clés uniques avec des noms, alors j'ai pensé à cela:
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.'
Voici mon 0.02 $. Il est basé sur la réponse d'Andrew Clark, un peu plus claire, et couvre également le cas où une chaîne à remplacer est une sous-chaîne d'une autre chaîne à remplacer (plus longue chaîne 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 cet Gist , n'hésitez pas à le modifier si vous avez une proposition.
À partir de Python 3.8
et de l'introduction de 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'
J'avais besoin d'une solution où les chaînes à remplacer peuvent être des expressions régulières, par exemple pour aider à normaliser un texte long en remplaçant plusieurs caractères d'espacement par un seul. En me basant sur une chaîne de réponses 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.'
Pour moi, l'essentiel est que vous puissiez également utiliser des expressions régulières, par exemple pour remplacer des mots entiers ou pour normaliser des espaces:
>>> 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, par ex. 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 rationnelles erronées parmi les clés de votre 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 que les remplacements ne sont pas enchaînés, mais exécutés simultanément. Cela le rend plus efficace sans limiter ce qu'il peut faire. Pour reproduire l’effet de l’enchaînement, il vous suffira d’ajouter davantage de paires de remplacement de chaîne et d’assurer l’ordre prévu des paires:
>>> multiple_replace("button", {"but": "mut", "mutton": "lamb"})
'mutton'
>>> multiple_replace("button", [("button", "lamb"),
... ("but", "mut"), ("mutton", "lamb")])
'lamb'
Voici un exemple qui est plus efficace sur les longues chaînes avec beaucoup de 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 concaténations de longues chaînes. Nous découpons la chaîne source en fragments, en remplaçant certains des fragments lors de la constitution de la liste, puis réunions le tout dans une chaîne.
Je ne connais pas la vitesse, mais voici ma solution miracle pour le 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 n ° 1 regex ci-dessus. Remarque - Si une nouvelle valeur est une sous-chaîne d'une autre, l'opération n'est pas commutative.
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
encore une fois, c’est très hacky et ce n’est pas quelque chose que vous devriez utiliser régulièrement. Mais il est bon de savoir que vous pouvez faire quelque chose comme ceci si vous en avez besoin.
Vous pouvez utiliser la bibliothèque pandas
et la fonction replace
qui prennent en charge les correspondances exactes ainsi que les remplacements de regex. 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 sont effectués avec l'ordre dans lequel ils apparaissent dans les listes
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
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]]
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)
Je suggère que le code devrait être, par exemple:
z = "My name is Ahmed, and I like coding "
print(z.replace(" Ahmed", " Dauda").replace(" like", " Love" ))
Il imprimera toutes les modifications comme demandé.
Ou juste 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)
À 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 pour effectuer 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 cela 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()