如何将回调传递给re.sub,但仍插入匹配捕获

  • 本文关键字:插入 sub 回调 re python regex
  • 更新时间 :
  • 英文 :


考虑:

text = "abcdef"
pattern = "(b|e)cd(b|e)"
repl = [r"1bla2", r"1blabla2"]
text = re.sub(pattern, lambda m: random.choice(repl), text)

我想用列表repl的条目随机替换匹配项。但当使用lambda m: random.choice(repl)作为回调时,它不再用捕获来替换12等,而是以纯文本形式返回"1bla2"

我试着在re.py中查找他们是如何在内部执行的,所以我可能能够调用相同的内部函数,但这似乎并不微不足道。

上面的示例返回a1bla2fa1blabla2f,而abblaefabblablaef在我的情况下是有效的选项。

请注意,我使用的是一个函数,因为在有几个像text = "abcdef abcdef"这样的匹配的情况下,它应该为每个匹配从repl中随机选择一个替换,而不是为所有匹配使用相同的替换。

如果传递一个函数,则会丢失对backreferences的自动转义。你只需要得到匹配的对象,就必须完成工作。所以你可以:

在正则表达式中选择一个字符串,而不是传递一个函数:

text = "abcdef"
pattern = "(b|e)cd(b|e)"
repl = [r"1bla2", r"1blabla2"]
re.sub(pattern, random.choice(repl), text)
# 'abblaef' or 'abblablaef'

或者编写一个处理匹配对象并允许更复杂处理的函数。您可以利用expand使用反向引用:

text = "abcdef abcdef"
pattern = "(b|e)cd(b|e)"
def repl(m):
repl = [r"1bla2", r"1blabla2"]           
return m.expand(random.choice(repl))

re.sub(pattern, repl, text)
# 'abblaef abblablaef' and variations

当然,您可以将该函数放入lambda:中

repl = [r"1bla2", r"1blabla2"]
re.sub(pattern, lambda m: m.expand(random.choice(repl)), text)

实现这一点(并确保随机替换(的一种方法是嵌套对re.sub:的调用

text = "abcdef abcdef"
pattern = "(b|e)cd(b|e)"
repl = [r"1bla2", r"1blabla2"]
text = re.sub(pattern, lambda m: re.sub(r'\(d+)', lambda m1: m.group(int(m1.group(1))), random.choice(repl)), text)
print(text)

之间的输出不同

abblaef abblaef
abblaef abblablaef
abblablaef abblaef
abblablaef abblablaef

事实证明,我的嵌套调用基本上相当于m.expand,正如Mark Meyer的回答中所描述的那样。

在该示例中,捕获组被放回原来的位置而没有更改。因此,请更改要使用的模式前瞻性和前瞻性断言:

replacements = ['bla', 'blabla']
re.sub(r"(?<=b|e)cd(?=b|e)", lambda mo:random.choice(replacements), text)

如果前面是b|e,后面是b|e,则与cd匹配。

或者,替换函数接收匹配对象,因此它可以访问所有匹配组:

re.sub(pattern, lambda mo:f"{mo[1]}{random.choice(replacements)}{mo[2]}", text)

其中mo是匹配对象,mo[1]是第一捕获组,mo[2]是第二捕获组。

最新更新