关于正则表达式替换,前面我写过一个应用:
python2代码搬运到python3要改很多print? 试试用pyCharm的正则表达式替换
其实这里的替换已经使用了分组的思想。
上面一行的匹配模式print (\S*)中,括号括起的部分匹配到的内容就被识别为匹配组1。而下一行的替换模式中,$1就指代了匹配组1的内容。
所以在这个例子里,匹配组1匹配到的内容是“123”,而在替换时,“123”就替换了$1对应的位置。
有时候,我们可能需要从一句话中提取多个分组,并且替换其中的全部,或者仅仅是部分几组。这个问题同样可以用正则表达式解决。这个方法是我在研究问答系统时琢磨出来的,所以我也以此作为例子:
现在,我们的问答系统需要回答这样一个问题:
曹丕的父亲是谁?
回答这个问题,要求我们把其中的“曹丕”和“父亲”提取出来(有时候也可以提取“谁”,用于限定答案的范围必须是一个人),然后就可以利用这两个条件在知识库中查找答案。
这样,这个问题就转化为用正则表达式提取其中的三个分组。下面是我为此写的一个正则表达式:
import re
quest = "曹丕的父亲是谁?"
template = re.compile(r"(\S[^的]*)的(\S[^是]*)是(\S[^?]*)?")
matches = re.search(template,quest)
if matches:
print(matches.group(0)) # full match
print(matches.group(1)) # match group1
print(matches.group(2)) # match group2
print(matches.group(3)) # match group3
结果
曹丕的父亲是谁?
曹丕
父亲
谁
而在回答时,我希望能够保持原句的语法,比如:
>>> answer("曹丕的父亲是谁?")
曹丕的父亲是曹操
这就意味着我们需要保留前两个分组,而把第三个分组用查找到的答案替换掉,假设已经查到答案,方法如下:
ans = "曹操"
re.sub(template,r"\1的\2是%s" % ans,quest)
曹丕的父亲是曹操
其中的\1,\2就表示第1、第2匹配组的内容(“曹丕”、“父亲”)。
问题词可以出现在不同位置,不过经过调整以后依然可以用正则表达式解决这问题,效果比如:
>>> answer("谁的父亲是曹操?")
曹彰的父亲是曹操
曹丕的父亲是曹操
曹植的父亲是曹操
曹昂的父亲是曹操
这是我实现的一个极简的基于知识库的问答系统的一部分,如果对其中的实现细节(包括正则表达式的适应性调整、知识图谱的查询SPARQL)感兴趣,可以在这里看到我更详细的jupyter notebook演示。