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 regex

PyPy 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可匹配ßcliff(這里的ff是一個字符)可匹配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表示單詞結束位置

 

(?|...|...)
重置分支匹配中的捕獲組編號。

>>> regex.match(r"(?|(first)|(second))", "first").groups()
('first',)
>>> regex.match(r"(?|(first)|(second))", "second").groups()
('second',)

兩次匹配都是把捕獲到的內容放到編號為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

部分匹配。可用於驗證用戶輸入,當輸入不合法字符時,立刻給出提示。

 

可以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 0Version 1兩個工作模式,其中的Version 0基本兼容現有的re模塊,以下是區別:

  Version 0  (基本兼容re模塊) Version 1
啟用方法

設置.VERSION0或.V0標志,或者在表達式里寫上(?V0)。

設置.VERSION1或.V1標志,或者在表達式里寫上(?V1)。

零寬匹配

像re模塊那樣處理:

.split 不能在零寬匹配處切割字符串。

.sub 在匹配零寬之后向前傳動一個位置。

PerlPCRE那樣處理

.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.V1

V1模式默認開啟.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) 記錄分離符

 

除此之外,可能還有未知的不兼容之處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM