python用re.sub實現分組匹配和替換(及問答系統中的應用)

  • 2019 年 10 月 30 日
  • 筆記

關於正則表達式替換,前面我寫過一個應用: 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演示。

Tags: