Как заменить несколько подстрок строки? - PullRequest
221 голосов
/ 25 мая 2011

Я хотел бы использовать функцию .replace для замены нескольких строк.

В настоящее время у меня есть

string.replace("condition1", "")

, но я хотел бы иметь что-то вроде

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

хотя это не похоже на хороший синтаксис

каков правильный способ сделать это?вроде как в grep / regex вы можете использовать \1 и \2 для замены полей на определенные строки поиска

Ответы [ 20 ]

225 голосов
/ 25 мая 2011

Вот краткий пример, который должен сделать трюк с регулярными выражениями:

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)

Например:

>>> pattern.sub(lambda m: rep[re.escape(m.group(0))], "(condition1) and --condition2--")
'() and --text--'
106 голосов
/ 25 мая 2011

Вы можете просто создать симпатичную маленькую функцию зацикливания.

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

, где text - полная строка, а dic - словарь - каждое определение - это строка, которая заменяет совпадение сterm.

Примечание : в Python 3 iteritems() заменено на items()


Осторожно: Словари Python donнет надежного заказа на итерацию.Это решение решает вашу проблему только в том случае, если:

  • порядок замен не имеет значения
  • нормально для замены изменить результаты предыдущих замен

Дляэкземпляр:

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

Возможный вывод # 1:

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

Возможный вывод # 2

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

Одним из возможных исправлений является использование 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)

Вывод:

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

Осторожно # 2: Неэффективно, если ваша строка text слишком велика или в словаре много пар.

82 голосов
/ 28 февраля 2012

Вот вариант первого решения, использующего Reduce, если вам нравится работать. :)

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

Мартино еще лучше:

repls = ('hello', 'goodbye'), ('world', 'earth')
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls, s)
70 голосов
/ 16 июня 2017

Почему бы не одно подобное решение?

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
31 голосов
/ 16 марта 2013

Это лишь краткий обзор великолепных ответов F.J и MiniQuark. Все, что вам нужно для множественной одновременной замены строк , это следующая функция:

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)

Использование:

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

Если вы хотите, вы можете сделать свои собственные специальные функции замены, начиная с этой более простой.

28 голосов
/ 05 марта 2013

Я построил это на превосходном ответе Ф.Дж .:

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)

Использование одного выстрела:

>>> 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é.

Обратите внимание, что поскольку замена выполняется всего за один проход, "café" меняется на "чай", но не возвращается обратно в "café".

Если вам нужно много раз выполнять одну и ту же замену, вы можете легко создать функцию замены:

>>> 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!\"

Улучшения:

  • превратил код в функцию
  • добавлена ​​многострочная поддержка
  • исправлена ​​ошибка в экранировании
  • Легко создать функцию для конкретной множественной замены

Наслаждайтесь! : -)

20 голосов
/ 25 мая 2011

Я хотел бы предложить использовать строковые шаблоны. Просто поместите строку, которую нужно заменить, в словарь, и все готово! Пример из 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'
11 голосов
/ 17 января 2016

В моем случае мне потребовалась простая замена уникальных ключей именами, поэтому я подумал:

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.'
8 голосов
/ 14 апреля 2016

Здесь мои $ 0,02. Он основан на ответе Эндрю Кларка, немного яснее, и также охватывает случай, когда заменяемая строка является подстрокой другой заменяемой строки (более длинные строки выигрывают)

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)

Именно в этой этой сущности не стесняйтесь изменять ее, если у вас есть какие-либо предложения.

6 голосов
/ 28 апреля 2019

Начиная с Python 3.8 и введением выражений присваивания (PEP 572) (оператор :=), мы можем применять замены в пределах списка:

# 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'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...