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

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

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

string.replace("condition1", "")

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

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

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

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

Ответы [ 20 ]

4 голосов
/ 20 мая 2014

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

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

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'
2 голосов
/ 01 января 2019

Я предлагаю, чтобы код был, например:

z = "My name is Ahmed, and I like coding "
print(z.replace(" Ahmed", " Dauda").replace(" like", " Love" ))

Он распечатает все изменения в соответствии с запросом.

1 голос
/ 25 мая 2011

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

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

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

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

1 голос
/ 25 мая 2011

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

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)

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

1 голос
/ 26 августа 2016

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

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, приведенный выше. Примечание. Если одно новое значение является подстрокой другого, операция не является коммутативной.

0 голосов
/ 16 апреля 2013

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

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)
0 голосов
/ 06 ноября 2015

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

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]] 
0 голосов
/ 19 августа 2015

это моё решение проблемы. Я использовал его в чат-боте для одновременной замены разных слов.

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

0 голосов
/ 17 июня 2014

Начиная с драгоценного ответа Эндрю, я разработал скрипт, который загружает словарь из файла и разрабатывает все файлы в открытой папке для выполнения замен.Скрипт загружает сопоставления из внешнего файла, в котором вы можете установить разделитель.Я новичок, но я нашел этот скрипт очень полезным при выполнении нескольких замен в нескольких файлах.Он загрузил словарь с более чем 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 голосов
/ 28 февраля 2014

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

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