《正則表達式經典實例(第2版)》——2.3 匹配多個字符之一
本節書摘來自異步社區《正則表達式經典實例(第2版)》一書中的第2章,第2.3節,作者: 【美】Jan Goyvaerts , Steven Levithan著,更多章節內容可以訪問雲棲社區“異步社區”公眾號查看
2.3 匹配多個字符之一
問題描述
創建一個正則表達式來匹配calendar的所有常見的錯誤拚寫形式,使你能夠在一份文檔中找到這個單詞而無需依賴作者的拚寫能力。在每個元音位置都允許使用a或者e。創建另外一個正則表達式來匹配一個單個的十六進製字符。再創建一個正則表達式來匹配不屬於十六進製字符的單個字符。
本節中的這個問題用於解釋一個重要的、經常使用的正則結構—字符組(character class)。
解決方案
錯誤拚寫的calendar
c[ae]l[ae]nd[ae]r
正則選項:無
正則流派:.NET、Java、JavaScript、PCRE、Perl、Python、Ruby
十六進製字符
[a-fA-F0-9]
正則選項:無
正則流派:.NET、Java、JavaScript、PCRE、Perl、Python、Ruby
非十六進製字符
[^a-fA-F0-9]
正則選項:無
正則流派:.NET、Java、JavaScript、PCRE、Perl、Python、Ruby
討論
使用方括號的表示法被稱作是一個字符組(character class)。一個字符組匹配在一個可能的字符列表中的單個字符。在第一個正則表達式中的3個字符組,每組可以匹配一個a或是一個e。它們彼此之間是獨立的。當你使用calendar來測試這個正則表達式的時候,第一個字符組匹配a,第二個字符組匹配e,第三個字符組會匹配a。
在一個字符組中,隻有4個字符擁有特殊功能:\、^、-和]。如果你使用的是Java或者 .NET,那麼左方括號[在字符組中也是一個元字符。反斜杠總是會對緊跟其後的字符進行轉義,這與它在字符組之外的作用一樣。被轉義的字符可以是單個字符,也可以是表示某個範圍的開始或結束。另外4個元字符隻有當它們被放置到特定位置時才擁有特殊含義。如果不放置在具有特殊含義的位置,那麼可以不必在字符組中進行轉義,直接把它們作為字麵字符來使用。例如,‹[][^-]›就可以在本書中除JavaScript以外所有流派中實現此用法。JavaScript將‹[]›視為空字符組,所以任何情況下都無法匹配。雖然如此,我們還是推薦你始終對這些元字符進行轉義,因此前麵的正則表達式應該始終使用‹[][^-]›形式。在使用中始終對元字符進行轉義會使你的正則表達式更加容易讓人理解。
所有其他字符均為字麵量,並且可以直接添加到字符組中。正則表達式‹[$()*+.?{|}›匹配方括號中9個字符中的任意一個。這9個字符僅在字符組外有特殊含義。字符組內它們僅視為字麵文本。將它們轉義隻會使正則表達式更加難以閱讀。
字母數字字符則不能使用反斜杠來轉義。如果這樣,要麼會出現一個錯誤,要麼會創建一個正則表達式記號(也就是在正則表達式中含有特殊含義的語法符號)。在前麵的實例2.2中,我們討論了一些其他正則表達式記號,其中提到了它們可以在字符組內使用。所有這些記號都由反斜杠和一個字母組成,有時候後麵還會跟一些其他字符。因此,‹[\r\n]›會匹配一個回車符(\r)或者換行符(\n)。
如果緊跟著左括號後麵是一個脫字符(^)的話,那麼就會排除整個字符組。也就是說它會匹配該字符組以外的任意字符。
在本書介紹的所有正則表達式流派中,不包含換行符的排除型字符組都會匹配換行符。請確保你的正則表達式不會無意中匹配多行文本。
連字符(-)被放到兩個字符之間的時候就會創建一個範圍(range)。該範圍包含連字符之前的字符、連字符之後的字符,以及按照字母表順序位於這兩個字符之間的所有字符。要想知道一個範圍中到底包含了哪些字符,請查看ASCII或者Unicode字符表。‹[A-z]›包含在ASCII表中大寫A到小寫z之間的所有字符。注意這個範圍中會包含一些標點符號,因此可以使用‹[A-Z[]^_`a-z]›來更加清晰地匹配相同的字符集合。我們推薦你所創建的範圍隻位於兩個數字,或者兩個同是大寫或者小寫的字母之間。
反向的範圍,如‹[z-a]›,是不允許的。
變體
簡寫
由反斜杠和一個字母組成的6個正則表達式記號會構成簡寫(shorthand)字符組:‹\d›、‹\D›、‹\w›、‹\W›、‹\s›和‹\S›。你可以在字符組之內或者之外使用這些簡寫。每個小寫的簡寫字符都擁有一個對應的大寫簡寫字符,其含義正好相反。
‹\d›和‹[\d]›都會匹配單個數字。‹\D›會匹配不是數字的任意字符,所以同‹[^\d]›是等價的。
下麵是我們使用簡寫‹\d›重寫本例前麵“十六進製字符”正則表達式:
[a-fA-F\d]
正則選項:無
正則流派:.NET、Java、PCRE、Perl、Python、Ruby
‹\w›匹配單個的單詞字符(word character)。所謂單詞字符指的是能夠出現在一個單詞中的字符。這包括了字母、數字和下劃線。這裏所選的字符集合看起來可能有些怪異,但是之所以這樣選的原因是這些字符正好是在編程語言中的標識符中通常允許使用的字符。‹\W›則會匹配不屬於上述字符集合的任意字符。
在Java 4至Java 6、JavaScript、PCRE和Ruby中,‹\w›總是與‹[a-zA-Z0-9]›的含義完全相同。而在 .NET中,它包含來自所有其他字母表(西裏爾語、泰語等)的字母和數字。在Java 7中,隻有設置了UNICODE\CHARACTER_CLASS標誌才會包含其他字母表的字符。在Python 2.x中,隻有當你在創建正則表達式時設置了UNICODE或U標誌時,才會包含其他字母表的字符。在Python 3.x中,默認包含其他字母表的字符,但可以使用ASCII或A標誌使‹\w›隻匹配ASCII編碼。在Perl 5.14中,/a(ASCII)標誌使‹\w›等同於‹[a-zA-Z0-9_]›,而/u(Unicode)標誌則加入所有Unicode字母,/l(local)則使‹\w›依所處地區而定。在Perl 5.14之前的版本中,以及在Perl 5.14中使用/d(default)或者未使用/adlu中任意標誌時,如果目標文本或正則表達式編碼為UTF-8,或者正則表達式包含了255以上碼位(如‹\x{100}›)或Unicode屬性(如‹\p{L}›),則‹\w›自動包含Unicode字母;否則,‹\w›默認僅匹配ASCII。
在上述這些流派中,‹\d› 遵循與‹\w›相同的規則。在.NET中,其他字母表中的數字總是會被包含進來,而在Python中則依據UNICODE和ASCII標誌,以及使用的是Python 2.x還是3.x。在Perl 5.14中,依據/adlu標誌。而Perl更早的版本中,依據目標文本和正則表達式的編碼,以及正則表達式是否有任何Unicode記號。
‹\s›匹配任意的空白字符(whitespace character)。其中包括了空格、製表符和換行符。‹\S›會匹配‹\s›不能匹配的任意字符。在.NET和JavaScript中,‹\s›也會匹配根據Unicode標準被定義為空白符號的字符。在Java、Perl和Python中,‹\s›遵循與‹\w›和‹\d›相同的規則。需要注意的是,JavaScript對於‹\s›使用Unicode,而對於‹\d›和‹\w›則使用ASCII標準。當我們還要考慮‹\b›的時候,就會遇到更多的不一致性。‹\b›不是一個簡寫字符組,而是一個字符邊界。雖然你可以期望當‹\w›支持Unicode的時候‹\b›也應該支持Unicode,而當‹\w›隻支持ASCII的時候,‹\b›也應該是隻使用ASCII,然而事實上卻不總是如此。在實例2.6中的“單詞字符”小節中會介紹更多的細節。
不區分大小寫
(?i)[A-F0-9]
正則選項:無
正則流派:.NET、Java、XRegExp、PCRE、Perl、Python、Ruby
(?i)[^A-F0-9]
正則選項:無
正則流派:.NET、Java、XRegExp、PCRE、Perl、Python、Ruby
不區分大小寫也會影響字符組,可以使用一個外部選項來設置(參見實例3.4),也可以在正則表達式內采用模式修飾符來設置(參見實例2.1)。上麵給出的這兩個正則表達式與最初的解答是等價的。
JavaScript也采用相同的規則,但是它並不支持‹(?i)›。要在JavaScript中把一個正則表達式設置為不區分大小寫,就需要在創建的時候設置/i標誌。或者使用支持正則表達式前設置模式修飾符的JavaScript XRegExp庫。
流派特有的特性
.NET字符組補集
[a-zA-Z0-9-[g-zG-Z]]
正則選項:無
正則流派:.NET 2.0或以後版本
這個正則表達式匹配一個十六進製數字,但是采用了一種迂回的方式。這裏的基字符組可以匹配任意字母數字字符,但是後麵的一個嵌套組則減去了從g到z的所有字母。這個嵌套組必須出現在基組的最後,緊跟在一個連字號之後:‹[class- [subtract]]›。
當采用了Unicode的屬性、區塊和字母表時,字符組補集(subtraction)會尤為有用。舉例來說,‹\p{IsThai}›會匹配在Thai語區塊中的任意字符。‹\P{N}›則匹配不擁有Number屬性的任意字符。把二者使用字符組差集組合起來,‹[\p{IsThai}-[\P{N}]]›就可以匹配10個泰語數字的任意字符。實例2.7中介紹了Unicode屬性更多細節。
Java字符組並集、交集和補集
Java允許把一個字符組嵌套在另外一個組中。如果嵌套的組是直接包含的,則結果是兩個字符組的並集(union)。可以嵌套任意多個組。正則表達式‹[a-f[A-F][0-9]]›和‹[a-f[A-F[0-9]]]›使用了字符組並集。它們匹配一個十六進製數字,與不包含額外方塊的形式相同。
正則表達式‹[\w&&[a-fA-F0-9\s]]›使用字符組交集匹配十六進製數字。如果舉辦一個正則表達式混淆大賽,那麼上麵這個式子很可能會得獎。這裏的基本字符組‹\w›可以匹配任意單詞字符。嵌套組‹[a-fA-F0-9\s]›則會匹配任意十六進製數字與任意空白字符。最後所得到的組則是這兩個組的交集(intersection),隻會匹配十六進製數字。因為基組並不匹配空白字符,而嵌套組不能匹配‹[g-zG-Z_]›,因此在最後的字符組中會去掉它們,隻保留十六進製數字。
‹[a-zA-Z0-9&&[^g-zG-Z]]›使用字符組補集匹配一個十六進製數字,它同樣也采用了一種迂回的方式。這裏的基本字符組‹[a-zA-Z0-9]›可以匹配任意字母數字字符,而隨後使用了一個嵌套組‹[^g-zG-Z]›來排除從g到z的所有字母。這個嵌套組必須是一個排除型字符組,並且要緊跟在兩個&符號之後:‹[class&&[^subtract]]›。
字符組交集和補集在使用到Unicode的屬性、區塊和字母表時,是非常有用的。依上所述,‹\p{InThai}›會匹配在Thai語區塊中的任意字符,而‹\p{N}›則會匹配擁有Number屬性的任意字符。那麼,使用正則字符組交集‹[\p{InThai}&&[\p{N}]]›就可以匹配10個泰語數字中的任意數字。
最後更新:2017-06-06 07:35:18