Как заменить несколько подстрок строки?
Я хотел бы использовать функцию .replace для замены нескольких строк.
В настоящее время у меня
string.replace("condition1", "")
но хотел бы иметь что-то вроде
string.replace("condition1", "").replace("condition2", "text")
хотя это не похоже на хороший синтаксис
Каков правильный способ сделать это? вроде как в grep/regex вы можете сделать \1
и \2
для замены полей на определенные строки поиска
Ответы
Ответ 1
Вот краткий пример того, что нужно делать с регулярными выражениями:
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--'
Ответ 2
Вы можете просто сделать хорошую маленькую функцию зацикливания.
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"}
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
строка слишком велика или в словаре много пар.
Ответ 3
Вот вариант первого решения с использованием сокращения, если вам нравится быть функциональным.:)
repls = {'hello' : 'goodbye', 'world' : 'earth'}
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls.iteritems(), s)
martineau даже лучшая версия:
repls = ('hello', 'goodbye'), ('world', 'earth')
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls, s)
Ответ 4
Почему бы не одно решение как это?
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
Ответ 5
Это лишь краткий обзор отличных ответов 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.'
Если вы хотите, вы можете сделать свои собственные специальные функции замены, начиная с этой более простой.
Ответ 6
Я построил это на превосходном ответе F.J.
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é".
Если вам нужно сделать такую же замену много раз, вы можете легко создать функцию замены:
>>> 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!\"
Улучшения:
- превратил код в функцию
- добавлена многострочная поддержка.
- исправлена ошибка при экранировании
- легко создать функцию для определенной множественной замены
Наслаждайтесь!: -)
Ответ 7
Я хотел бы предложить использовать строковые шаблоны. Просто поместите строку, которая будет заменена в словаре, и все будет установлено! Пример из 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'
Ответ 8
В моем случае мне потребовалась простая замена уникальных ключей именами, поэтому я подумал:
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.'
Ответ 9
Здесь мои $ 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)
Именно в этой этой сути, не стесняйтесь изменять его, если у вас есть какие-либо предложения.
Ответ 10
Начиная 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'
Ответ 11
Мне нужно решение, в котором строки, которые нужно заменить, могут быть регулярными выражениями,
например, чтобы помочь в нормализации длинного текста, заменив несколько пробельных символов на один. Основываясь на цепочке ответов от других, включая 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'
Ответ 12
Здесь образец, который более эффективен для длинных строк со многими небольшими заменами.
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)
Дело в том, чтобы избежать многих конкатенаций длинных строк. Мы прерываем исходную строку до фрагментов, заменяя некоторые фрагменты по мере формирования списка, а затем присоединяем все это обратно к строке.
Ответ 13
Я не знаю о скорости, но это мое быстрое решение:
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 regex выше. Примечание. Если одно новое значение является подстрокой другого, то операция не является коммутативной.
Ответ 14
Вы действительно не должны этого делать, но я просто считаю это слишком круто:
>>> replacements = {'cond1':'text1', 'cond2':'text2'}
>>> cmd = 'answer = s'
>>> for k,v in replacements.iteritems():
>>> cmd += ".replace(%s, %s)" %(k,v)
>>> exec(cmd)
Теперь answer
является результатом всех замен в свою очередь
это very хакки и не то, что вы должны регулярно использовать. Но просто приятно знать, что вы можете сделать что-то подобное, если вам когда-нибудь понадобится.
Ответ 15
Или просто для быстрого взлома:
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)
Ответ 16
Вот еще один способ сделать это со словарем:
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)
Ответ 17
Начиная с драгоценного ответа Андрея, я разработал script, который загружает словарь из файла и разрабатывает все файлы в открытой папке для замены. script загружает сопоставления из внешнего файла, в который вы можете установить разделитель. Я новичок, но я нашел этот script очень полезным при выполнении нескольких замещений в нескольких файлах. Он загрузил словарь с более чем 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()
Ответ 18
Это мое решение проблемы. Я использовал его в чате, чтобы сразу заменить разные слова.
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
Ответ 19
Другой пример:
Список ввода
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]]
Ответ 20
Я предлагаю, чтобы код был, например:
z = "My name is Ahmed, and I like coding "
print(z.replace(" Ahmed", " Dauda").replace(" like", " Love" ))
Он распечатает все изменения в соответствии с запросом.
Ответ 21
Вы можете использовать библиотеку 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
Вы можете найти пример здесь. Обратите внимание, что замены текста выполняются в порядке их появления в списках.