Как заменить несколько подстрок строки?

284

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

У меня сейчас

string.replace("condition1", "")

но хотелось бы что то типа

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

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

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

НКИ
источник
7
Вы пробовали все предоставленные решения? Какой из них быстрее?
tommy.carstensen
Я потратил время, чтобы проверить все ответы в разных сценариях. См. Stackoverflow.com/questions/59072514/…
Пабло
1
Честно говоря, я предпочитаю твой цепной подход всем остальным. Я приземлился здесь в поисках решения и использовал ваш, и он работает просто отлично.
frakman1
@ frakman1 +1. Понятия не имею, почему за это больше не голосуют. Все остальные методы затрудняют чтение кода. Если бы была замена массивов проходов функции, это сработало бы. Но ваш цепочечный метод наиболее понятен (по крайней мере, со статическим числом замен)
IceFire

Ответы:

269

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

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--'
Эндрю Кларк
источник
7
Замена происходит за один проход.
Эндрю Кларк
26
dkamins: это не слишком умно, это даже не так умно, как должно быть (мы должны с помощью regex-экранировать ключи, прежде чем соединять их с "|"). почему это не слишком силен? потому что таким образом мы делаем это в один проход (= быстро), и мы делаем все замены в то же время, избегая столкновения , как "spamham sha".replace("spam", "eggs").replace("sha","md5")быть "eggmd5m md5"вместо"eggsham md5"
летающих овец
8
@AndrewClark Я был бы очень признателен, если бы вы могли объяснить, что происходит в последней строке с лямбдой.
минералы
11
Привет, я создал небольшую суть с более ясной версией этого фрагмента. Это также должно быть немного более эффективным: gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
bgusach
15
Для python 3 используйте items () вместо iteritems ().
Джангари
127

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

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

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

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


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

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

Например:

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

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

«Это моя свинья, и это моя свинья».

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

«Это моя собака, а это моя свинья».

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

Вывод:

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

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

Джозеф Хансен
источник
37
Порядок, в котором вы применяете различные замены, будет иметь значение, поэтому вместо использования стандартного dict рассмотрите возможность использования OrderedDict- или списка из двух кортежей.
ленивец
5
Это делает повторение строки дважды ... не очень хорошим для исполнения.
Валентин Лоренц
6
По производительности он хуже, чем говорит Валентин, - он будет проходить по тексту столько раз, сколько есть элементов в dic! Хорошо, если «текст» маленький, но ужасный для большого текста.
JDonner
3
Это хорошее решение для некоторых случаев. Например, я просто хочу добавить 2 символа, и мне все равно, в каком порядке они идут, потому что ключи подстановки не соответствуют никаким значениям. Но я хочу, чтобы было ясно, что происходит.
Натан Гарабедян
5
Обратите внимание, что это может дать неожиданные результаты, потому что вновь вставленный текст в первой итерации может совпадать во второй итерации. Например, если мы наивно попытаемся заменить все «A» на «B» и все «B» на «C», строка «AB» будет преобразована в «CC», а не «BC».
Амброз Бизжак
106

Почему бы не одно решение, как это?

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
Это супер полезно, просто и переносимо.
Shred
Выглядело хорошо, но не заменял регулярные выражения, как в: для r в ((r '\ s.', '.'), (R '\ s,', ',')):
Мартин
2
сделать его 1-строчным: ss = [s.replace (* r) для r in ((«коричневый», «красный»), («ленивый», «быстрый»))] [0]
Марк К
95

Вот вариант первого решения, использующего 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)
Бьорн Линдквист
источник
8
Было бы проще составить replsпоследовательность кортежей и покончить с iteritems()вызовом. т.е. repls = ('hello', 'goodbye'), ('world', 'earth')и reduce(lambda a, kv: a.replace(*kv), repls, s). Также будет работать без изменений в Python 3.
martineau
отлично! если вы используете python3, используйте элементы вместо iteritems (теперь удаляются в элементах dicts).
e.arbitrio
2
@martineau: Это неправда, что это работает без изменений в python3, так reduceкак был удален .
норманиус
5
@normanius: reduceвсе еще существует, однако он был включен в functoolsмодуль (см. документацию ) в Python 3, поэтому, когда я сказал «без изменений», я имел в виду тот же код, который можно было запустить, хотя, по общему признанию, он потребовал reduceбы importредактирования в случае необходимости. так как он больше не является встроенным.
Мартино
35

Это лишь краткий обзор отличных ответов FJ и 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.'

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

MMJ
источник
1
Несмотря на то, что это хорошее решение, параллельные замены строк не дадут точно таких же результатов, как их последовательное выполнение (цепочка), хотя это может не иметь значения.
Мартино
2
Конечно, со rep_dict = {"but": "mut", "mutton": "lamb"}строкой это "button"приводит к "mutton"вашему коду, но даст, "lamb"если замены будут связаны, один за другим.
Мартино
2
Это главная особенность этого кода, а не дефект. С цепочечными заменами он не мог достичь желаемого поведения замены двух слов одновременно и взаимно, как в моем примере.
ММД
1
Это может показаться отличной особенностью, если вам это не нужно. Но здесь речь идет об одновременных заменах , тогда это действительно главная особенность. При «зацепленных» заменах результат в примере будет таким Do you prefer cafe? No, I prefer cafe., который вовсе не желателен.
ММД
@ Давид напиши свой собственный ответ, твоя редакция слишком радикальна
UmNyobe
29

Я построил это на превосходном ответе FJs:

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

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

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

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

Улучшения:

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

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

MiniQuark
источник
1
Может кто-нибудь объяснить это шаг за шагом для таких нубов, как я?
Джулиан Суарес
Товарищ питон нуб, так что я собираюсь сделать неполный выстрел, чтобы понять это .. а. разбить key_values ​​на вещи для замены (ключи, соединенные «|») и логику (если совпадение является ключом, возвращаемое значение) b. создайте синтаксический анализатор регулярных выражений («шаблон», который ищет ключи и использует данную логику) - оберните это в лямбда-функцию и верните. Вещи, которые я смотрю сейчас: re.M, и необходимость лямбда для замены логики.
Фокс
1
@Fox Вы получили это. Вы можете определить функцию вместо того, чтобы использовать лямбду, это просто сделать код короче. Но обратите внимание, что pattern.subожидается функция с одним параметром (текст для замены), поэтому функция должна иметь доступ к ней replace_dict. re.Mразрешает многострочные замены (это хорошо объяснено в doc: docs.python.org/2/library/re.html#re.M ).
MiniQuark
22

Я хотел бы предложить использовать строковые шаблоны. Просто поместите строку, которую нужно заменить, в словарь, и все готово! Пример из 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'
Фредрик Пихл
источник
Выглядит хорошо, но при добавлении ключа, не предусмотренного в, substituteвозникает исключение, поэтому будьте осторожны при получении шаблонов от пользователей.
Барт Фридрихс
2
Недостаток этого подхода заключается в том, что шаблон должен содержать все, и не более всех, $ strings для замены, см. Здесь
RolfBly
17

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

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
Это работает до тех пор, пока у вас нет замены. Если вы заменили iс sвами бы получить странное поведение.
bgusach
1
Если порядок значим, вместо указанного выше вы можете использовать массив: b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) тогда, если вы будете осторожны при упорядочении пар массивов, вы можете быть уверены, что не используете replace () рекурсивно.
КОД-ЧИТАНИЕ
Похоже, что теперь дикты поддерживают порядок , начиная с Python 3.7.0. Я протестировал его, и он работает по порядку на моей машине с последней стабильной версией Python 3.
Джеймс Косс
15

Начав 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'
Ксавье Гихот
источник
Знаете ли вы, если это более эффективно, чем использовать замену в цикле? Я проверяю все ответы на производительность, но у меня еще нет 3,8.
Пабло
Почему я получаю вывод в списке?
johnrao07
1
@ johnrao07 Ну, понимание списка строит список. Вот почему в этом случае вы получаете ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']. Но выражение присваивания ( text := text.replace) также итеративно создает новые версии text, изменяя его. После понимания списка вы можете использовать textпеременную, которая содержит измененный текст.
Ксавье Гихот
1
Если вы хотите вернуть новую версию textв виде однострочного текста, вы также можете использовать [text := text.replace(a, b) for a, b in replacements][-1](обратите внимание на [-1]), который извлекает последний элемент понимания списка; то есть последняя версия text.
Ксавье Гихот
13

Здесь мои $ 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)

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

bgusach
источник
1
Вместо этого это должен был быть принятый ответ, потому что регулярное выражение составлено из всех ключей, сортируя их в порядке убывания длины и соединяя их с | оператор чередования регулярных выражений. И сортировка необходима для того, чтобы выбрать самый длинный из всех возможных вариантов, если есть какие-либо альтернативы.
Сачин С.
Я согласен, что это лучшее решение, благодаря сортировке. Кроме того, сортировка идентична моему первоначальному ответу, поэтому я позаимствовал сортировку и для своего решения, чтобы никто не пропустил такую ​​важную функцию.
MMJ
6

Мне нужно было решение, в котором заменяемые строки могут быть регулярными выражениями, например, чтобы помочь нормализовать длинный текст, заменив несколько пробельных символов одним. Основываясь на цепочке ответов от других, включая 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"

Если вы хотите использовать словарные ключи как обычные строки, вы можете избежать их перед вызовом multip_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"

Следующая функция может помочь в нахождении ошибочных регулярных выражений среди ключей вашего словаря (поскольку сообщение об ошибке из множественного_реклама не очень красноречиво):

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'

источник
Это хорошо, спасибо. Можно ли его улучшить, чтобы в заменах можно было использовать обратные ссылки? Я не сразу понял, как это добавить.
cmarqu
Ответ на мой вопрос выше - stackoverflow.com/questions/45630940/…
cmarqu
4

Вот пример, который более эффективен на длинных строках с множеством небольших замен.

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)

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

9000
источник
2

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

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

Теперь answerрезультат всех замен по очереди

Опять же, это очень хакерское и не то, что вы должны регулярно использовать. Но просто приятно знать, что вы можете сделать что-то подобное, если вам когда-нибудь понадобится.

inspectorG4dget
источник
2

Я тоже боролся с этой проблемой. Со многими подстановками регулярные выражения борются и примерно в четыре раза медленнее, чем циклы string.replace(в моих условиях эксперимента).

Вы должны обязательно попробовать использовать библиотеку Flashtext ( запись в блоге здесь , Github здесь ). В моем случае это было чуть более чем на два порядка быстрее, от 1,8 с до 0,015 с (регулярные выражения занимали 7,7 с) для каждого документа.

Легко найти примеры использования по ссылкам выше, но это рабочий пример:

    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)

Обратите внимание, что Flashtext выполняет замены за один проход (чтобы избежать a -> b и b -> c, переводящих «a» в «c»). Flashtext также ищет целые слова (поэтому «is» не будет соответствовать «th is »). Это прекрасно работает, если ваша цель состоит из нескольких слов (замена «Это» на «Привет»).

Pablo
источник
Как это работает, если вам нужно заменить HTML-теги? Например, заменить <p>на /n. Я попробовал ваш подход, но с тэгами flashtext, похоже, не разбирает его?
alias51
1
Я не уверен, почему это не работает, как вы ожидаете. Одна возможность состоит в том, что эти теги не разделены пробелами, и помните, что Flashtext ищет целые слова. Чтобы обойти это, сначала используйте простую замену, так что «Hi <p> ​​там» становится «Hi <p> ​​там». Вы должны быть осторожны, чтобы удалить ненужные пробелы, когда вы закончите (также просто заменить?). Надеюсь, это поможет.
Пабло
Спасибо, вы можете установить <и >отметить конец слова (но быть включенным в замену)?
alias51
1
Я считаю, что «слова» отмечены только пробелами. Возможно, есть некоторые дополнительные параметры, которые вы можете установить в «KeywordProcessor». В противном случае рассмотрите подход выше: замените «<» на «<», примените Flashtext, затем замените обратно (в вашем случае, например, «<» на «<» и «\ n» на «\ n» может работать).
Пабло
2

Мне кажется, что для полноты этого вопроса нужен однострочный рекурсивный лямбда-ответ. Так что:

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

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

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

Ноты:

  • Это потребляет входной словарь.
  • Диктофоны Python сохраняют ключевой порядок начиная с 3.6; соответствующие предостережения в других ответах больше не актуальны. Для обратной совместимости можно прибегнуть к версии на основе кортежей:
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

Примечание: Как и для всех рекурсивных функций в python, слишком большая глубина рекурсии (т. Е. Слишком большие словари замещения) приведет к ошибке. Смотрите, например, здесь .

mcsoini
источник
Я сталкиваюсь с RecursionError при использовании большого словаря!
Пабло
@Pablo Интересно. На сколько огромный? Обратите внимание, что это происходит для всех рекурсивных функций. Смотрите, например, здесь: stackoverflow.com/questions/3323001/…
mcsoini
Мой словарь подстановок близок к 100 тыс. Терминов ... на данный момент наилучшим подходом является использование string.replace .
Пабло
1
@Pablo в этом случае вы не можете использовать рекурсивные функции. В общем, sys.getrecursionlimit()это пара 1000, макс. используйте цикл или что-то в этом роде или попробуйте упростить подстановки.
Максоини
Да, я боюсь, что здесь действительно нет ярлыков.
Пабло
1

Я не знаю о скорости, но это мое быстрое решение:

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

... но мне нравится ответ № 1 регулярное выражение выше. Примечание: если одно новое значение является подстрокой другого, то операция не является коммутативной.

del_hol
источник
1

Вы можете использовать pandasбиблиотеку и replaceфункцию, которая поддерживает как точные совпадения, так и замены регулярных выражений. Например:

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))

И измененный текст:

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

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

Джордж Пипис
источник
1

Для замены только одного символа используйте translateи str.maketransмой любимый метод.

тл; др> result_string = your_string.translate(str.maketrans(dict_mapping))


демонстрация

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.
Carson
источник
0

Начиная с драгоценного ответа Эндрю, я разработал сценарий, который загружает словарь из файла и разрабатывает все файлы в открытой папке для выполнения замен. Скрипт загружает сопоставления из внешнего файла, в котором вы можете установить разделитель. Я новичок, но я нашел этот скрипт очень полезным при выполнении нескольких замен в нескольких файлах. Он загрузил словарь с более чем 1000 записей в секундах. Это не элегантно, но это сработало для меня

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

это мое решение проблемы. Я использовал его в чате, чтобы заменить разные слова сразу.

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"})

это станет The cat hunts the dog

emorjon2
источник
0

Другой пример: список ввода

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

Желаемый результат будет

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

Код:

[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

Или просто для быстрого взлома:

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

Вот еще один способ сделать это с помощью словаря:

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)
Стефан Грюнвальд
источник