【问题标题】:Multiple regex substitutions using a dict with regex expressions as keys使用带有正则表达式作为键的 dict 进行多个正则表达式替换
【发布时间】:2021-05-22 00:04:32
【问题描述】:

我想使用多个正则表达式对一个字符串进行多次替换。我还想一次性进行替换,以避免创建字符串的多个实例。

假设我想在下面进行替换,同时避免多次使用 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() 函数正确地找到了表达式 "(?&lt;=o)a" 的匹配项,因此现在必须在字典中找到它以返回其替换,但 match.group(0) 提交的字典查找值是对应的匹配字符串"a"。在字典中搜索match.re(即产生匹配的表达式)也不起作用,因为它的值是从字典键编译的整个不相交的表达式(即"(?&lt;=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"

【问题讨论】:

标签: python regex string substitution re


【解决方案1】:

如果您想使用的表达式没有匹配空字符串(如果您想替换,这是一个有效的假设),您可以在 |ing 表达式之前使用组,然后检查哪个组找到了匹配项:

(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

【讨论】:

    【解决方案2】:

    您可以通过将您的密钥保持为预期匹配并将您的替换和正则表达式存储在嵌套的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'
    

    【讨论】:

    • 使用这种方法,我将如何处理具有多个正则表达式条件的替换?例如,如果我想在“o”之前将“a”替换为“w”,但在“c”之前替换为“x”?
    • @BarnabyClunge 另一个答案(现已编辑)说明了这一点。使用 thejonny 提供的答案时,已经测试并且您上面的示例有效。
    猜你喜欢
    • 2013-05-29
    • 1970-01-01
    • 2019-01-24
    • 2014-03-29
    • 1970-01-01
    • 1970-01-01
    • 2015-02-21
    • 2016-04-08
    • 2022-12-24
    相关资源
    最近更新 更多