使用以正则表达式作为键的字典进行多个正则表达式替换
Multiple regex substitutions using a dict with regex expressions as keys
我想使用多个正则表达式对一个字符串进行多次替换。我还想在一次传递中进行替换,以避免创建字符串的多个实例。
假设我想在下面进行替换,同时避免多次使用 re.sub(),无论是显式还是循环:
import re
text = "local foals drink cola"
text = re.sub("(?<=o)a", "w", text)
text = re.sub("l(?=a)", "co", text)
print(text) # "local fowls drink cocoa"
为此,我找到的最接近的解决方案是从替换目标字典中编译正则表达式,然后使用 lambda 函数将每个匹配的目标替换为其在字典中的值。但是,这种方法在使用元字符时不起作用,因此在此示例中删除了正则表达式所需的功能。
让我先用一个没有元字符的例子来演示:
import re
text = "local foals drink cola"
subs_dict = {"a":"w", "l":"co"}
subs_regex = re.compile("|".join(subs_dict.keys()))
text = re.sub(subs_regex, lambda match: subs_dict[match.group(0)], text)
print(text) # "coocwco fowcos drink cocow"
现在观察将所需的元字符添加到字典键会导致 KeyError:
import re
text = "local foals drink cola"
subs_dict = {"(?<=o)a":"w", "l(?=a)":"co"}
subs_regex = re.compile("|".join(subs_dict.keys()))
text = re.sub(subs_regex, lambda match: subs_dict[match.group(0)], text)
>>> KeyError: "a"
原因是 sub() 函数正确地找到了表达式 "(?<=o)a"
的匹配项,因此现在必须在字典中找到它以 return 它的替换,但是值match.group(0)
提交的字典查找是相应的匹配字符串 "a"
。在字典中搜索 match.re
也不起作用(即产生匹配的表达式),因为它的值是从字典键编译的整个不相交表达式(即 "(?<=o)a|l(?=a)"
) .
编辑:如果有人看到使用 lambda 函数实现的 thejonny 解决方案尽可能接近我的原始解决方案,那么它会像这样工作:
import re
text = "local foals drink cola"
subs_dict = {"(?<=o)a":"w", "l(?=a)":"co"}
subs_regex = re.compile("|".join("("+key+")" for key in subs_dict))
group_index = 1
indexed_subs = {}
for target, sub in subs_dict.items():
indexed_subs[group_index] = sub
group_index += re.compile(target).groups + 1
text = re.sub(subs_regex, lambda match: indexed_subs[match.lastindex], text)
print(text) # "local fowls drink cocoa"
如果您要使用的表达式都没有匹配空字符串(如果您想替换,这是一个有效的假设),您可以在 |
表达式之前使用组,然后检查哪个组找到了比赛:
(exp1)|(exp2)|(exp3)
或者命名组,这样您就不必计算子表达式中的子组。
替换功能可以查看匹配的组,并从列表中选择替换。
我想到了这个实现:
import re
def dictsub(replacements, string):
"""things has the form {"regex1": "replacement", "regex2": "replacement2", ...}"""
exprall = re.compile("|".join("("+x+")" for x in replacements))
gi = 1
replacements_by_gi = {}
for (expr, replacement) in replacements.items():
replacements_by_gi[gi] = replacement
gi += re.compile(expr).groups + 1
def choose(match):
return replacements_by_gi[match.lastindex]
return re.sub(exprall, choose, string)
text = "local foals drink cola"
print(dictsub({"(?<=o)a":"w", "l(?=a)":"co"}, text))
打印 local fowls drink cocoa
您可以通过将密钥保持为预期的匹配并将替换和正则表达式存储在嵌套的 dict
中来实现。鉴于您要匹配特定字符,此定义应该有效。
subs_dict = {"a": {'replace': 'w', 'regex': '(?<=o)a'}, 'l': {'replace': 'co', 'regex': 'l(?=a)'}}
subs_regex = re.compile("|".join([subs_dict[k]['regex'] for k in subs_dict.keys()]))
re.sub(subs_regex, lambda match: subs_dict[match.group(0)]['replace'], text)
'local fowls drink cocoa'
我想使用多个正则表达式对一个字符串进行多次替换。我还想在一次传递中进行替换,以避免创建字符串的多个实例。
假设我想在下面进行替换,同时避免多次使用 re.sub(),无论是显式还是循环:
import re
text = "local foals drink cola"
text = re.sub("(?<=o)a", "w", text)
text = re.sub("l(?=a)", "co", text)
print(text) # "local fowls drink cocoa"
为此,我找到的最接近的解决方案是从替换目标字典中编译正则表达式,然后使用 lambda 函数将每个匹配的目标替换为其在字典中的值。但是,这种方法在使用元字符时不起作用,因此在此示例中删除了正则表达式所需的功能。
让我先用一个没有元字符的例子来演示:
import re
text = "local foals drink cola"
subs_dict = {"a":"w", "l":"co"}
subs_regex = re.compile("|".join(subs_dict.keys()))
text = re.sub(subs_regex, lambda match: subs_dict[match.group(0)], text)
print(text) # "coocwco fowcos drink cocow"
现在观察将所需的元字符添加到字典键会导致 KeyError:
import re
text = "local foals drink cola"
subs_dict = {"(?<=o)a":"w", "l(?=a)":"co"}
subs_regex = re.compile("|".join(subs_dict.keys()))
text = re.sub(subs_regex, lambda match: subs_dict[match.group(0)], text)
>>> KeyError: "a"
原因是 sub() 函数正确地找到了表达式 "(?<=o)a"
的匹配项,因此现在必须在字典中找到它以 return 它的替换,但是值match.group(0)
提交的字典查找是相应的匹配字符串 "a"
。在字典中搜索 match.re
也不起作用(即产生匹配的表达式),因为它的值是从字典键编译的整个不相交表达式(即 "(?<=o)a|l(?=a)"
) .
编辑:如果有人看到使用 lambda 函数实现的 thejonny 解决方案尽可能接近我的原始解决方案,那么它会像这样工作:
import re
text = "local foals drink cola"
subs_dict = {"(?<=o)a":"w", "l(?=a)":"co"}
subs_regex = re.compile("|".join("("+key+")" for key in subs_dict))
group_index = 1
indexed_subs = {}
for target, sub in subs_dict.items():
indexed_subs[group_index] = sub
group_index += re.compile(target).groups + 1
text = re.sub(subs_regex, lambda match: indexed_subs[match.lastindex], text)
print(text) # "local fowls drink cocoa"
如果您要使用的表达式都没有匹配空字符串(如果您想替换,这是一个有效的假设),您可以在 |
表达式之前使用组,然后检查哪个组找到了比赛:
(exp1)|(exp2)|(exp3)
或者命名组,这样您就不必计算子表达式中的子组。
替换功能可以查看匹配的组,并从列表中选择替换。
我想到了这个实现:
import re
def dictsub(replacements, string):
"""things has the form {"regex1": "replacement", "regex2": "replacement2", ...}"""
exprall = re.compile("|".join("("+x+")" for x in replacements))
gi = 1
replacements_by_gi = {}
for (expr, replacement) in replacements.items():
replacements_by_gi[gi] = replacement
gi += re.compile(expr).groups + 1
def choose(match):
return replacements_by_gi[match.lastindex]
return re.sub(exprall, choose, string)
text = "local foals drink cola"
print(dictsub({"(?<=o)a":"w", "l(?=a)":"co"}, text))
打印 local fowls drink cocoa
您可以通过将密钥保持为预期的匹配并将替换和正则表达式存储在嵌套的 dict
中来实现。鉴于您要匹配特定字符,此定义应该有效。
subs_dict = {"a": {'replace': 'w', 'regex': '(?<=o)a'}, 'l': {'replace': 'co', 'regex': 'l(?=a)'}}
subs_regex = re.compile("|".join([subs_dict[k]['regex'] for k in subs_dict.keys()]))
re.sub(subs_regex, lambda match: subs_dict[match.group(0)]['replace'], text)
'local fowls drink cocoa'