1. 程式人生 > >正則表示式——常用量詞

正則表示式——常用量詞

{m,n}是通用形式的量詞,正則表示式還有三個常用量詞,分別是+、?、*。它們的形態雖然不同於{m,n},功能卻是相同的(也可以把它們理解為"量詞簡記法"),具體說明見表2-2。

表2-2  常用量詞

常用量詞

{m,n}等價形式

說明

*

{0,}

可能出現,也可能不出現,出現次數沒有上限

+

{1,}

至少出現1次,出現次數沒有上限

?

{0,1}

至多出現1次,也可能不出現

在實際應用中,在很多情況只需要表示這三種意思,所以常用量詞的使用頻率要高於{m,n},下面分別說明。

大家都知道,美國英語和英國英語有些詞的寫法是不一樣的,比如traveler和traveller,如果希望"通吃"traveler和traveller,就要求第2個l是"至多出現1次,也可能不出現"的,正好使用?量詞:travell?er,如例2-4所示。

例2-4  量詞?的應用

re.search(r"^travell?er$", "traveler") != None  #  => True  
re.search(r"^travell?er$", "traveller") != None # => True

其實這樣的情況還有很多,比如favor和favour、color和colour。此外還有很多其他應用場合,比如http和https,雖然是兩個概念,但都是協議名,可以用https?匹配;再比如表示價格的字串,有可能是100也有可能是¥100,可以用¥?100匹配 。

量詞也廣泛應用於解析HTML程式碼。HTML是一種"標籤語言",它包含各種各樣的tag(標籤),比如<head>、<img>、<table>等,這些tag的名字各異,形式卻相同:從<開始,到>結束,在<和>之間有若干字元,"若干"的意思是長度不確定,但不能為0(<>並不是合法的tag),也不能是>字元 。如果要用一個正則表示式匹配所有的tag,需要用<匹配開頭的<,用>匹配結尾的>,用[^>]+匹配中間的"若干字元",所以整個正則表示式就是<[^>]+>,程式如例2-5所示。

例2-5  量詞+的應用

re.search(r"^<[^>]+>$", "<bold>") != None       #  => True  
re.search(r"^<[^>]+>$", "</table>") != None     #  => True  
re.search(r"^<[^>]+>$", "<>") != None           #  => False  

類似的,也可以使用正則表示式匹配雙引號字串。不同的是,雙引號字串的兩個雙引號之間可以沒有任何字元,""也是一個完全合法的雙引號字串,應該使用量詞*,於是整個正則表示式就成了"[^"]*",程式見例2-6。

例2-6  量詞*的應用

re.search(r"^\"[^\"]*\"$", "\"some\"") != None  #  => True  
re.search(r"^\"[^\"]*\"$", "\"\"") != None      #  => True

注:字串之中表示雙引號需要轉義寫成\",這並不是正則表示式中的規定,而是為字串轉義考慮。

量詞的使用有很多學問,不妨多看幾個tag匹配的例子:tag可以粗略分為open tag和close tag,比如<head>就是open tag,而</html>就是close tag;另外還有一類標籤是self-closing tag,比如<br/>。現在來看分別匹配這三類tag的正則表示式。

open tag的特點是以<開頭,然後是"若干字元"(但不能以/開頭),最後是>,所以對應的正則表示式是<[^/][^>]*>;注意:因為[^/]必須匹配一個字元,所以"若干字元"中其他部分必須寫成[^>]*,否則它無法匹配名字為單個字元的標籤,比如<b>。

close tag的特點是以<開頭,之後是/字元,然後是"若干字元(但不能以/開頭)",最後是>,所以對應的正則表示式是</[^>]+>;

self-closing tag的特點是以<開頭,中間是"若干字元",最後是/>,所以對應的正則表示式是<[^>]+/>。注意:這裡不是<[^>/]+/>,排除型字元組只排除>,而不排除/,因為要確認的只是在結尾的>之前出現/,如果寫成<[^>/]+/>,則要求tag內部不能出現/,就無法匹配<img src="http://somehost/picture" />這類的tag了。

表2-3列出了匹配幾類tag的表示式。

表2-3  各類tag的匹配

匹配所有tag的表示式

tag分類

匹配分類tag的表示式

<[^>]+>

open tag

<[^/>][^>]*>

close tag

</[^>]+>

self-closing tag

<[^>/]+/>

對比表格中"匹配所有tag的表示式"和"匹配分類tag的表示式",可以發現它們的模式是相近的,只是細節上有差異。也就是說,通過變換字元組和量詞,可以準確控制正則表示式能匹配的字串的範圍,達到不同的目的。這其實是使用正則表示式時的一條根本規律:使用合適的結構(包括字元組和量詞),精確表達自己的意圖,界定能匹配的文字。

再仔細觀察,你或許會發現,匹配open tag的表示式,也可以匹配self-closing tag:<[^/][^>]*>能夠匹配<br/>,因為[^>]*並不排除對/的匹配。那麼將表示式改為<[^/][^>]*[^/]>,就保證匹配的open tag不會以/>結尾了。

不過這會產生新的問題:<[^/][^>]*[^/]>能匹配的tag,在<和>之間出現了兩個[^/],上一章已經講過,排除型字元組表示"在當前位置,匹配一個沒有列出的字元",所以tag裡的字串必須至少包含兩個字元,這樣就無法匹配<u>了。

仔細想想,真正要表達的意思是,在tag內部的字串不能以/開頭,也不能以/結尾,如果這個字串只包含一個字元,那麼它既是開頭,又是結尾,使用兩個排除型字元組顯然是不合適的,看起來沒辦法解決了。實際上,只是現有的知識還不足夠解決這個問題而已,在第68頁有這個問題的詳細解法。