Мені потрібно було рішення, де рядки, які слід замінити, можуть бути звичайними виразами, наприклад, щоб допомогти нормалізувати довгий текст, замінивши кілька символів пробілу на один. Спираючись на ланцюжок відповідей інших, включаючи MiniQuark і mmj, ось що я придумав:
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)
Це працює для прикладів, наведених в інших відповідях, наприклад:
>>> 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.'
Головне для мене, що ви можете також використовувати регулярні вирази, наприклад, замінити лише цілі слова або нормалізувати пробіл:
>>> 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"
Якщо ви хочете використовувати клавіші словника як звичайні рядки, ви можете уникнути цих, перш ніж викликати multiple_replace, використовуючи, наприклад, цю функцію:
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"
Наступна функція може допомогти у знаходженні помилкових регулярних виразів серед ваших словникових клавіш (оскільки повідомлення про помилку від multiple_replace не дуже вказує):
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())
Зауважте, що він не ланцюговує заміни, а виконує їх одночасно. Це робить його більш ефективним, не обмежуючи, що він може зробити. Щоб імітувати ефект ланцюжка, можливо, вам просто потрібно буде додати більше пар заміни рядків і забезпечити очікуване впорядкування пар:
>>> multiple_replace("button", {"but": "mut", "mutton": "lamb"})
'mutton'
>>> multiple_replace("button", [("button", "lamb"),
... ("but", "mut"), ("mutton", "lamb")])
'lamb'