這兩天在寫爬蟲程式,涉及英文文字處理,需要規範化英文標點符號的寫法。正常情況下,英文句號「.」後面需要保證有且只有一個空格,但也有例外情況,比如「i.e.」、「e.g.」、「P.S.」這種。由於無法預測大小寫,因此在正規表示式中使用了「標誌位」flags,卻死活不生效。
一開始,我的函式是這樣寫的:
1 def punctuate(s): 2 #----其餘程式碼暫略 3 s = re.sub(' e. g. ', 'e.g.', s, re.I) 4 return s
程式碼的本意是:本來好好的「e.g.」,被函式前半斷的程式碼錯改成「e. g. 」之後,需要修復一下,將英文句號「.」後面的空格刪掉。但這行 re.sub() 程式碼主要有2個問題:
- 「e. g.」前後不一定是空格,因此這樣寫的話,如果遇到「e. g.,」或是「(e. g. xxx」的情況就會被跳過。
- 英文的句號「.」未轉義
- 標誌位 re.I 不生效
前2個問題好解決。改進程式碼如下:
1 def punctuate(s): 2 #----其餘程式碼暫略 3 s = re.sub('([^a-zA-Z]e\.) (g\.[^a-zA-Z])', '\g<1>\g<2>', s, re.I) 4 return s
規則是:「e. g.」之前或之後,必須有「非英文字母」的字元(包括空格),且「e.」和「g.」中間有一個空格,則將中間的空格刪掉,且保留前後的「非英文字母」(\g<1>表示查詢到的第1個括號內的文字,\g<2>表示第2個括號)。但標誌位 re.I 的問題還是沒解決。
後來翻到了「Python--詳解Python中re.sub」這篇文章,才頓悟:re.sub() 函式有5個引數,我傳入了4個引數,最後一個被認為是第4個引數,而不是第5個!多麼低階的錯誤啊!
查閱「Python官方文件」可知,
re.sub(pattern, repl, string, count=0, flags=0)
我傳入的第4個引數 re.I 會被當作是 count。因此,正確的姿勢是明確寫明「flags=re.I」。
整個標點符號規範化函式還包括其它的替換,完整程式碼如下:
1 def punctuate(s): 2 s = re.sub('([,:;?!\.”\)])', '\g<1> ', s) #後加空格 3 s = re.sub('([“\(])', ' \g<1>', s) #前加空格 4 s = re.sub('([“\(]) ', '\g<1>', s) #後刪空格 5 s = re.sub(' ([,:;?!\.”\)])', '\g<1>', s) #前刪空格 6 s = re.sub('([,\.?!;\)]) ”', '\g<1>”', s) #閉引號前去空格 7 s = re.sub('\) ([,:;?!\.”])', ')\g<1>', s) #閉括號後去空格 8 s = re.sub('(\d)\. (\d)', '\g<1>.\g<2>', s) #小數點後去空格 9 s = re.sub(' +', ' ', s) #多空格改單空格 10 #拉丁加點縮寫單詞,點號後面去空格 11 s = re.sub('([^a-zA-Z]e\.) (g\.[^a-zA-Z])', '\g<1>\g<2>', s, flags=re.I) 12 s = re.sub('([^a-zA-Z]i\.) (e\.[^a-zA-Z])', '\g<1>\g<2>', s, flags=re.I) 13 s = re.sub('([^a-zA-Z]q\.) (v\.[^a-zA-Z])', '\g<1>\g<2>', s, flags=re.I) 14 s = re.sub('([^a-zA-Z]v\.) (s\.[^a-zA-Z])', '\g<1>\g<2>', s, flags=re.I) 15 s = re.sub('([^a-zA-Z]n\.) (b\.[^a-zA-Z])', '\g<1>\g<2>', s, flags=re.I) 16 s = re.sub('([^a-zA-Z]p\.) (s\.[^a-zA-Z])', '\g<1>\g<2>', s, flags=re.I) 17 s = re.sub('\. ,', '.,', s) 18 return s
多麼痛的領悟!