python用re.sub实现分组匹配和替换(及问答系统中的应用)

版权声明:本文为博主原创文章,欢迎转载但请声明来源。 https://blog.csdn.net/blmoistawinde/article/details/81839647

关于正则表达式替换,前面我写过一个应用:
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演示。

猜你喜欢

转载自blog.csdn.net/blmoistawinde/article/details/81839647
今日推荐