考虑:
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)
作为回调时,它不再用捕获来替换1
、2
等,而是以纯文本形式返回"1bla2"
。
我试着在re.py中查找他们是如何在内部执行的,所以我可能能够调用相同的内部函数,但这似乎并不微不足道。
上面的示例返回a1bla2f
或a1blabla2f
,而abblaef
或abblablaef
在我的情况下是有效的选项。
请注意,我使用的是一个函数,因为在有几个像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]
是第二捕获组。