Как я могу выполнять несколько замещений с помощью regex в python?
Я могу использовать этот код ниже для создания нового файла с заменой a
на aa
с использованием регулярных выражений.
import re
with open("notes.txt") as text:
new_text = re.sub("a", "aa", text.read())
with open("notes2.txt", "w") as result:
result.write(new_text)
Мне было интересно, нужно ли мне использовать эту строку, new_text = re.sub("a", "aa", text.read())
, несколько раз, но подставлять строку для других букв, которые я хочу изменить, чтобы изменить более чем одну букву в тексте?
То есть, так a
→ aa
, b
→ bb
и c
→ cc
.
Поэтому мне нужно написать эту строку для всех букв, которые я хочу изменить, или есть более простой способ. Возможно, создать "словарь" переводов. Должен ли я помещать эти буквы в массив? Я не уверен, как позвонить им, если я это сделаю.
Ответы
Ответ 1
Ответ, предложенный @nhahtdh, действителен, но я бы сказал, что он менее pythonic, чем канонический пример, который использует код менее непрозрачный, чем его манипуляции с регулярным выражением, и использует преимущества встроенных структур данных и функции анонимной функции python.
В этом контексте имеет смысл словарь переводов. На самом деле, как это делает Python Cookbook, как показано в этом примере (скопировано из ActiveState http://code.activestate.com/recipes/81330-single-pass-multiple-replace/)
import re
def multiple_replace(dict, text):
# Create a regular expression from the dictionary keys
regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
# For each match, look-up corresponding value in dictionary
return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
if __name__ == "__main__":
text = "Larry Wall is the creator of Perl"
dict = {
"Larry Wall" : "Guido van Rossum",
"creator" : "Benevolent Dictator for Life",
"Perl" : "Python",
}
print multiple_replace(dict, text)
Итак, в вашем случае вы можете сделать dict trans = {"a": "aa", "b": "bb"}
, а затем передать его в multiple_replace
вместе с текстом, который вы хотите перевести. В основном вся эта функция создает одно огромное регулярное выражение, содержащее все ваши регулярные выражения для перевода, а затем, когда его можно найти, передавая лямбда-функцию в regex.sub
для выполнения поиска словаря перевода.
Вы можете использовать эту функцию во время чтения из своего файла, например:
with open("notes.txt") as text:
new_text = multiple_replace(replacements, text.read())
with open("notes2.txt", "w") as result:
result.write(new_text)
Я фактически использовал этот точный метод в производстве, в случае, когда мне нужно было перевести месяцы года с чешского языка на английский для задания веб-скрепок.
Как отмечал @nhahtdh, одним из недостатков этого подхода является то, что он не является префиксом: словарные ключи, которые являются префиксами других ключей словаря, вызовут прерывание метода.
Ответ 2
Вы можете использовать группу захвата и обратную ссылку:
re.sub(r"([characters])", r"\1\1", text.read())
Поместите символы, которые вы хотите удвоить, между []
. Для случая нижнего регистра a
, b
, c
:
re.sub(r"([abc])", r"\1\1", text.read())
В строке замены вы можете ссылаться на все, что соответствует группе захвата ()
с обозначением \n
, где n
- это некоторое положительное целое число (исключено). \1
относится к первой группе захвата. Существует другое обозначение \g<n>
, где n
может быть любым неотрицательным целым числом (0 разрешено); \g<0>
будет ссылаться на весь текст, соответствующий выражению.
Если вы хотите удвоить все символы, кроме новой строки:
re.sub(r"(.)", r"\1\1", text.read())
Если вы хотите удвоить все символы (включая новую строку):
re.sub(r"(.)", r"\1\1", text.read(), 0, re.S)
Ответ 3
Используя советы от как сделать "строгий" класс, мы можем сделать объект идентичным строке, но для дополнительного метода sub
:
import re
class Substitutable(str):
def __new__(cls, *args, **kwargs):
newobj = str.__new__(cls, *args, **kwargs)
newobj.sub = lambda fro,to: Substitutable(re.sub(fro, to, newobj))
return newobj
Это позволяет использовать шаблон построителя, который выглядит лучше, но работает только для предопределенного числа подстановок. Если вы используете его в цикле, больше нет смысла создавать дополнительный класс. Например.
>>> h = Substitutable('horse')
>>> h
'horse'
>>> h.sub('h', 'f')
'forse'
>>> h.sub('h', 'f').sub('f','h')
'horse'
Ответ 4
Вы можете использовать библиотеку 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
Вы можете найти пример здесь
Ответ 5
Я обнаружил, что мне пришлось изменить код Emmett J. Butler, изменив функцию лямбда, чтобы использовать myDict.get(mo.group(1), mo.group(1)). Исходный код не работал у меня; использование myDict.get() также обеспечивает преимущество значения по умолчанию, если ключ не найден.
OIDNameContraction = {
'Fucntion':'Func',
'operated':'Operated',
'Asist':'Assist',
'Detection':'Det',
'Control':'Ctrl',
'Function':'Func'
}
replacementDictRegex = re.compile("(%s)" % "|".join(map(re.escape, OIDNameContraction.keys())))
oidDescriptionStr = replacementDictRegex.sub(lambda mo:OIDNameContraction.get(mo.group(1),mo.group(1)), oidDescriptionStr)