Avevo bisogno di una soluzione in cui le stringhe da sostituire possono essere espressioni regolari, ad esempio per aiutare a normalizzare un testo lungo sostituendo più caratteri di spazi bianchi con uno solo. Basandomi su una catena di risposte da altri, inclusi MiniQuark e mmj, questo è quello che ho pensato:
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)
Funziona per gli esempi forniti in altre risposte, ad esempio:
>>> 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.'
La cosa principale per me è che puoi usare anche espressioni regolari, ad esempio per sostituire solo parole intere o per normalizzare lo spazio bianco:
>>> 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"
Se vuoi usare le chiavi del dizionario come stringhe normali, puoi evitarle prima di chiamare multiple_replace usando ad esempio questa funzione:
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 seguente funzione può aiutare a trovare espressioni regolari errate tra le chiavi del dizionario (poiché il messaggio di errore da multiple_replace non è molto indicativo):
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())
Si noti che non concatena le sostituzioni, ma le esegue contemporaneamente. Questo lo rende più efficiente senza limitare ciò che può fare. Per imitare l'effetto del concatenamento, potrebbe essere necessario aggiungere più coppie di sostituzione delle stringhe e garantire l'ordinamento previsto delle coppie:
>>> multiple_replace("button", {"but": "mut", "mutton": "lamb"})
'mutton'
>>> multiple_replace("button", [("button", "lamb"),
... ("but", "mut"), ("mutton", "lamb")])
'lamb'