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演示。
