Python的regex模块——更强大的正则表达式引擎
Python自帶了正則表達式引擎(內置的re模塊),但是不支持一些高級特性,比如下面這幾個:
- 固化分組??? Atomic grouping
- 占有優先量詞 ?? Possessive quantifiers
- 可變長度的逆序環視 ?? Variable-length lookbehind
- 遞歸匹配??? Recursive patterns
- (起始/繼續)位置錨\G??? Search anchor
幸好,在2009年,Matthew Barnett寫了一個更強大正則表達式引擎——regex模塊,這是一個Python的第三方模塊。
除了上面這幾個高級特性,還有很多有趣、有用的東西,本文大致介紹一下,很多內容取自regex的文檔。
?
無論是編程還是文本處理,regex模塊都是一件利器。
用一個指標可以大致了解它的復雜性,re模塊有4270行C語言代碼,而regex模塊有24513行C語言代碼。
?
這個模塊以后可能被收進Python標準庫。目前(2015年)它還在不斷發展,經常發布bug修正版,不過感覺用在一般生產環境應該沒什么問題。
?
目錄
一、安裝regex
二、一些有趣的特性
三、模糊匹配
四、兩種工作模式
五、Version 0模式和re模塊不兼容之處
?
一、安裝regex
regex支持Python 2.5+和Python 3.1+,可以用pip命令安裝:
pip install regexPyPy 2.6+也可以使用這個模塊。
?
regex基本兼容re模塊,現有的程序可以很容易切換到regex模塊:
import regex as re?
二、一些有趣的特性
完整的Unicode支持
1,支持最新的Unicode標準,這一點經常比Python本身還及時。
2,支持Unicode代碼屬性,包括scripts和blocks。
????? 如:\p{Cyrillic}表示西里爾字符(scripts),\p{InCyrillic}表示西里爾區塊(blocks)。
3,支持完整的Unicode字符大小寫匹配,詳見此文。
????? 如:ss可匹配?;cli?(這里的?是一個字符)可匹配CLIFF(FF是兩個字符)。
????? 不需要的話可以關閉此特性。不支持Unicode組合字符與單一字符的大小寫匹配,所以感覺這個特性不太實用。
4,regex.WORD標志開啟后:
?? ?? 作用1:\b、\B采用Unicode的分界規則,詳見此文。
?????????????????? 如:開啟后\b.+?\b可搜索到3.4;關閉后小數點.成為分界符,于是只能搜到['3', '.', '4']。
? ? ? 作用2:采用Unicode的換行符。除了傳統的\r、\n,Unicode還有一些換行符,開啟后作用于.MULTILINE和.DOTALL模式。
5,\X匹配Unicode的單個字形(grapheme)。
????? Unicode有時用多個字符組合在一起表示一個字形,詳見此文。
????? \X匹配一個字形,如:^\X$可以匹配'\u0041\u0308'。
?
單詞起始位置、單詞結束位置
\b是單詞分界位置,但不能區分是起始還是結束位置。
regex用\m表示單詞起始位置,用\M表示單詞結束位置。
?
(?|...|...)
重置分支匹配中的捕獲組編號。
兩次匹配都是把捕獲到的內容放到編號為1捕獲組中,在某些情況很方便。
?
(?flags-flags:...)
局部范圍的flag控制。在re模塊,flag只能作用于整個表達式,現在可以作用于局部范圍了:
>>> regex.search(r"<B>(?i:good)</B>", "<B>GOOD</B>") <regex.Match object; span=(0, 11), match='<B>GOOD</B>'>在這個例子里,忽略大小寫模式只作用于標簽之間的單詞。
(?i:)是打開忽略大小寫,(?-i:)則是關閉忽略大小寫。
如果有多個flag挨著寫既可,如(?is-f:),減號左邊的是打開,減號右邊的是關閉。
?
除了局部范圍的flag,還有全局范圍的flag控制,如?(?si-f)<B>good</B>
re模塊也支持這個,可以參見Python文檔。
把flags寫進表達式、而不是以函數參數的方式聲明,方便直觀且不易出錯。
?
(?(DEFINE)...)
定義可重復使用的子句
>>> regex.search(r'(?(DEFINE)(?P<quant>\d+)(?P<item>\w+))(?&quant) (?&item)', '5 elephants') <regex.Match object; span=(0, 11), match='5 elephants'>此例中,定義之后,(?&quant)表示\d+,(?&item)表示\w+。如果子句很復雜,能省不少事。
?
partial matches
部分匹配??捎糜隍炞C用戶輸入,當輸入不合法字符時,立刻給出提示。
?
可以pickle編譯后的正則表達式對象
如果正則表達式很復雜或數量很多,編譯需要較長時間。
這時可以把編譯好的正則式用pickle存儲到文件里,下次使用直接pickle.load()就不必再次編譯了。
?
除了以上這些,還有很多新特性(匹配控制、便利方法等等),這里就不介紹了,請自行查閱文檔。
?
三、模糊匹配
regex有模糊匹配(fuzzy matching)功能,能針對字符進行模糊匹配,提供了3種模糊匹配:
- i,模糊插入
- d,模糊刪除
- s,模糊替換
以及e,包括以上三種模糊
?
舉個例子:
>>> regex.findall('(?:hello){s<=2}', 'hallo') ['hallo'](?:hello){s<=2}的意思是:匹配hello,其中最多容許有兩個字符的錯誤。
于是可以成功匹配hallo。
?
這里只簡單介紹一下模糊匹配,詳情還是參見文檔吧。
?
四、兩種工作模式
regex有Version 0和Version 1兩個工作模式,其中的Version 0基本兼容現有的re模塊,以下是區別:
| ? | Version 0? (基本兼容re模塊) | Version 1 |
| 啟用方法 | 設置.VERSION0或.V0標志,或者在表達式里寫上(?V0)。 | 設置.VERSION1或.V1標志,或者在表達式里寫上(?V1)。 |
| 零寬匹配 | 像re模塊那樣處理: .split?不能在零寬匹配處切割字符串。 .sub 在匹配零寬之后向前傳動一個位置。 | 像Perl和PCRE那樣處理: .split?可以在零寬匹配處切割字符串。 .sub 采用正確的行為。 |
| 內聯flag | 內聯flag只能作用于整個表達式,不可關閉。 | 內聯flag可以作用于局部表達式,可以關閉。 |
| 字符組 | 只支持簡單的字符組。 | 字符組里可以有嵌套的集合,也可以做集合運算(并集、交集、差集、對稱差集)。 |
| 大小寫匹配 | 默認支持普通的Unicode字符大小寫,如Й可匹配й。 這與Python3里re模塊的默認行為相同。 | 默認支持完整的Unicode字符大小寫,如ss可匹配?。 可惜不支持Unicode組合字符與單一字符的大小寫匹配,所以感覺這個特性不太實用??梢栽诒磉_式里寫上(?-f)關閉此特性。 |
如果什么設置都不做,會采用regex.DEFAULT_VERSION指定的模式。在目前,regex.DEFAULT_VERSION的默認值是regex.V0。
如果想默認使用V1模式,這樣就可以了:
import regex regex.DEFAULT_VERSION = regex.V1V1模式默認開啟.FULLCASE(完整的忽略大小寫匹配)。通常用不上這個,所以在忽略大小寫匹配時用(?-f)關閉.FULLCASE即可,這樣速度更快一點,例如:(?i-f)tag
?
其中零寬匹配的替換操作差異比如明顯。絕大多數正則引擎采用的是Perl流派的作法,于是Version 1也朝著Perl的方向改過去了。
>>> # Version 0 behaviour (like re) >>> regex.sub('(?V0).*', 'x', 'test') 'x' >>> regex.sub('(?V0).*?', '|', 'test') '|t|e|s|t|'>>> # Version 1 behaviour (like Perl) >>> regex.sub('(?V1).*', 'x', 'test') 'xx' >>> regex.sub('(?V1).*?', '|', 'test') '|||||||||'re模塊對零寬匹配的實現可能是有誤的(見issue1647489);
而V0零寬匹配的搜索和替換會出現不一致的行為(搜索采用V1的方式,替換采用re模塊的方式);
在Python 3.7+環境下,re和regex模塊的行為同時做了改變,據稱是采用了“正確”的方式處理零寬匹配:總是返回第一個匹配(字符串或零寬),但是如果是零寬并且之前匹配的還是零寬則忽略。這和3.6-的re模塊、regex的V0模式、V1模式都略有不同。
?
說著挺嚇人的,在實際使用中3.6- re、3.7+ re、V0、V1之間極少出現不兼容的現象。
?
五、Version 0模式和re模塊不兼容之處
上面說了“Version 0基本兼容re模塊”,說說不兼容的地方:
?
1、對零寬匹配的處理。
regex修復了re模塊的搜索bug(見issue1647489),但是也帶來了不兼容的問題。
?
在re中,用".*?"搜索"test"返回:['', '', '', '', ''],也就是:最前、字母之間的3個位置、最后,總共5個位置。
在regex中,則返回:['', 't', '', 'e', '', 's', '', 't', '']
在實際使用中,這個問題幾乎不會造成不兼容的情況,所以基本可以忽略此差異。
?
2、\s的范圍。
在re中,\s在這一帶的范圍是0x09 ~ 0x0D,0x1C ~ 0x1E。
在regex中,\s采用的是Unicode 6.3+標準的\p{Whitespace},在這一帶的范圍有所縮小,只有:0x09 ~ 0x0D。
| 十六進制 | 十進制 | 英文說明 | 中文說明 |
| 0x09 | 9 | HT (horizontal tab) | 水平制表符 |
| 0x0A | 10 | LF (NL line feed, new line) | 換行鍵 |
| 0x0B | 11 | VT (vertical tab) | 垂直制表符 |
| 0x0C | 12 | FF (NP form feed, new page) | 換頁鍵 |
| 0x0D | 13 | CR (carriage return) | 回車鍵 |
| ?... | ?... | ?... | ?... |
| 0x1C | 28 | FS (file separator) | 文件分割符 |
| 0x1D | 29 | GS (group separator) | 分組符 |
| 0x1E | 30 | RS (record separator) | 記錄分離符 |
?
除此之外,可能還有未知的不兼容之處。
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Python的regex模块——更强大的正则表达式引擎的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 美团搜索中NER技术的探索与实践
- 下一篇: linux系统下如何去掉多余的^M