讀古今文學網 > 精通正則表達式(第3版) > Perl的正則流派 >

Perl的正則流派

Perl\'s Regex Flavor

下一頁的表7-2概要描述Perl的正則風格。以前,Perl的許多元字符是其他系統不支持的,但是經過許多年之後,其他系統接受了許多Perl的創新。這些常見的特性在第3章的概覽裡有描述,但是 Perl 還有專屬於自己的元素,會在本章後面講解(表 7-2 覆蓋了將要講解的各個元素)

下面是對表格的補充:

○1b只有在字符組內部才是退格符的簡記法。在字符組外部,b表示單詞分界符(☞133)。

八進制轉義接收2到3位的數值。

「xnum」十六進制轉義接收兩位數字(也可以是一位數字,但是會報警)。「x{num}」能接收任意長度的十六進制數。

表7-2:Perl的正則流派概覽

○2w、d、s之類完全支持Unicode。

Perl的s不能匹配ASCII的垂直製表符(☞115)。

○3 Perl的Unicode支持針對的是Unicode Version 4.1.0。

Unicode字母表也能支持。字母表和屬性名可以有『Is』前綴,但並非必須(☞125)。區塊名可以有『In』前綴,但只有在區塊和字母表的名字發生衝突時才必須使用。

Perl也支持「p{L&}」、「p{Any}」、「p{All}、「p{Assigned}」和「p{Unassigned}」屬性。Perl 支持例如「p{Letter}」的長屬性名。名字的各個單詞之間可能是空格、下畫線,或者什麼也沒有,(例如「p{Lowercase_Letter}」,也可能寫作「p{Lowercase Letter}」)或者是「p{Lowercase·Letter}」),為了前後一致,我推薦使用第123頁表格中的長命名。

「p{^…}」等價於「P{…}」。

○4 單詞分界符完全支持Unicode。

○5 順序環視可能包含捕獲型括號。

逆序環視中的子表達式必須匹配固定長度的文本。

○6/x修飾符只能識別ASCII空白字符。/m只對換行符有影響,而且不是所有的Unicode

換行符。

/i能夠在Unicode中正常工作。

所有的元字符並不是生而平等的。「正則元字符」沒有得到正則引擎的支持,但Perl的正則文字預處理機制能對付。

正則運算符和正則文字

Regex Operands and Regex Literals

表7-2最下面的條目標注有「專屬於正則文字」。正則文字(regex literal)就是m/regex/部分中的「regex」,雖然平時稱其為「正則表達式」,但在『/』分隔符之間的部分是有自己的解析規則。用Perl的行話來說,正則文字就是「表示正則含義的雙引號字符串(regex-aware double-quoted string)」,及處理之後傳遞給正則引擎的結果。正則文字處理機制提供了特殊的功能來構建正則表達式。

舉例來說,正則文字提供了變量插值功能。如果變量$num的值是20,代碼m/:.{$num}:/得到的就是「:.{20}:」。這樣可以根據需要即時構建正則表達式。正則文字的另一功能是大小寫自動切換展開,U…E 可以保證其中的字母均為大寫。比如,m/abcUxyzE/得到正則表達式「abcXYZ」。這個例子有點做作,如果需要使用「abcXYZ」,應該直接輸入m/abcXYZ/,但是這種功能結合變量插值就很有用:如果變量$tag 包含字符串「title」,則代碼得到

除正則文字之外還有什麼呢?我們可以把字符串(或者任何表達式)當作正則運算元,比如:

當$MatchField用作=~的運算元時,它的值就被解釋(interpreted)為正則表達式。這裡只能「解釋」普通的正則表達式,所以不支持只作用於正則文字的變量插值和「Q…E」。

下面的例子值得思考,如果把:

$text=~$MatchField

替換為:

$text=~m/$MatchField/

結果完全一樣。這裡的正則文字只包含一個元素——變量$MatchField。正則文字中插值變量的值不會被當作正則文字處理,所以變量內的U…E和$var之類不會被識別(第292頁說明了正則文字的處理細節)。

如果正則表達式在程序的執行期間需要多次用到,那麼正則運算元採用字符串或變量插值的效率差距就很明顯。第348頁討論了這個問題。

正則文字支持的特性

正則文字提供下面的特性:

●變量插值 正則表達式中以$和@開頭的變量會被替換為實際變量的值。$變量插入一個簡單的純量值(scalar value)。以@開頭的插入數組或者數組的一部分,以空格分隔各個元素(其實是以$〞變量作分隔符,它的默認值是空格)。

在Perl中,『%』引入一個散列變量(hash variable),但是在字符串中插入一個散列變量並沒有太大的意義,所以Perl不支持%插值。

●命名的Unicode字符 如果程序中包含「use charnames\':full\';」,就可以用N{name}序列引用Unicode字符。例如,N{LATIN SMALL LETTER SHARP S}匹配「s」。在Perl的unicore 目錄下的UnicodeData.txt中可以找到Perl支持的Unicode字符列表。下面的代碼能夠報告文件的位置:

「use charnames\':full\';」很容易忘記,或者忘記在『full』前面添加冒號,果真如此的話,N{…}就不能正常工作。同樣,如果使用了下面介紹的正則表達式重載,N{…}也不能正常工作。

●大小寫轉換前綴l和u能夠把後面的字符轉換為小寫或大寫形式。通常我們使用此功能來轉換插值變量的第一個字符。舉例來說,如果變量$title 包含「mr.」,m/…u$title…/就能生成正則表達式「…Mr.…」。Perl 的 lcfirst()和 ucfirst()函數提供了同樣的功能。

●大小寫轉換範圍L和U能夠把後面所有的字符轉換為小寫或大寫,其作用範圍一直到表達式末尾,或是E為止。同樣是$title,m/…U$titleE…/會產生正則表達式「…MR.…」。Perl的lc()和uc()函數提供了同樣的功能。

我們可以把這兩者結合起來:無論變量$title 採用怎樣的字母組合,m/…Lu$titleE…/都會得到「…Mr.…」。

●文字文本範圍Q「轉義(quote)」正則表達式元字符(也就是在它們之前放一個反斜線,保證它們只作為普通的字符),其作用範圍直到字符串的結尾,或者直到E。它能轉義正則表達式元字符,但不能轉義表示變量插值的正則文字U,當然也不能轉義E。奇怪的是,如果反斜線開頭的字符序列不能識別——例如F或者H,反斜線也不會被轉義。即使是Q…E,這樣的序列也會導致「unrecognized escape」警告。

在實踐中,這些限制並不是嚴重的缺陷,Q…E 通常用於引用插值文本,這樣就可以正確轉義所有的元字符。例如,如果$title包含「Mr.」,那麼代碼m/…Q$titleE…/就會生成正則表達式「…Mr.…」,我們要的就是這樣——希望匹配的是$title中的字符,而不是$title中的正則表達式。

如果你希望在正則表達式中包含某些用戶輸入的數據,這非常有用。舉例來說,m/Q$UserInputE/i能夠對$UserInput(作為字符串,而不是正則表達式)中的字符進行不區分大小寫的搜索。

Perl的函數quotemeta()提供了與Q…E等價的功能。

●重載 借助重載,用戶可以使用任何期望的方式預處理正則文字的文字字符。這是概念值得討論,但是目前的實現還有諸多限制。關於重載的細節討論請參見第341頁。

使用自己的正則表達式分隔符

Perl語法中最奇妙(也是最有用)的特性之一就是用戶可以使用自己的正則文字分隔符。傳統的分隔符是斜線,例如 m/…/、s/…/…/和 qr/…/,不過還可以使用除數字、字母和空格之外的字符。常用的包括:

右邊四個是特殊的分隔符:

●右邊的四個例子具有不同的開始-結束分隔符,而且可能嵌套(也就是說,如果開始-結束分隔符匹配恰當,表達式中容許包含與分隔符一樣的字符)。因為圓括號和方括號在正則表達式中經常用到,m(…)和m[…]可能不如其他更有吸引力。使用/x修飾符時,可能出現下面的形式:

也可以使用某種組合標記 regex,另一組(如果你喜歡,也可以用同樣的)標記replacement。例如:

如果這樣做了,就可以在兩對分隔符之間插入空格和註釋。第 319 頁進一步講解了substitution運算符的replacement運算元。

●對match運算符來說,把問號作為分隔符有其特殊價值(禁止更多的匹配),這一點在下一節講解關於match運算符時討論(☞308)。

●288頁已經提到,正則文字被解析成「表示正則含義的雙引號字符串」。如果用單引號作分隔符,就無法使用這些功能。使用 m\'…\'時就不會進行變量插值,實時修改文本的結構(比如Q…E)不會生效,N{…}也無法使用。也許在使用包含多個@的正則表達式時m\'…\'很有價值,因為這樣可以不需要轉義。

如果進行match操作,而分隔符是斜線或者問號,可以省略m,也就是:

是等價的。但我更喜歡明確寫上m。

正則文字的解析方式

How Regex Literals Are Parsed

大多數情況下,如果用戶「只會用到」上文講解的正則文字特性,就不需要理解 Perl 將它們轉換為真正的正則表達式的具體細節。就這一點來說,Perl直觀性非常方便,但是許多時候,瞭解細節並無壞處。下面列出了各種處理的順序:

1.找到結束分隔符,讀入修飾符(例如/i之類)。下面的處理就能判斷是否採用了/x之類的模式。

2.變量插值。

3.如果使用了正則表達式重載,正則文字的每個部分都會交給重載子程序來處理。各部分由插值變量分隔;插入的值是無法重載的。

如果正則表達式沒有進行重載,處理N{…}。

4.應用大小寫轉換結構(例如Q…E)。

5.把結果提交給正則引擎。

以上是程序員眼中的處理,但是 Perl 內部的處理其實是很複雜的。單單是第二步,就必須識別正則表達式的元字符,比如不應把「this$|that$」下畫線的那部分識別為變量。

正則修飾符

Regex Modifiers

Perl的正則運算符容許在正則文字的結束分隔符之後添加正則修飾符(例如m/…/i、s/…/…/i和qr/…/i中的i)。所有運算符都支持的核心修飾符一共有5種,詳見表7-3。

頭四種在第 3 章已經介紹過,它們能夠作為模式修飾符(☞135)或者範圍模式修飾符(☞135),在正則表達式之中使用。如果正則表達式內部出現了修飾符,match運算符也用到了修飾符,則正則表達式內部的修飾符的優先級更高(從另一方面來說就是,一旦修飾符應用到正則表達式內部的某些元素,這些元素就不再受其他修飾符的影響)。

表7-3:所有正則運算符可用到的核心修飾符

第五個核心修飾符/o,與效率有很大的關係。此問題從第348頁開始討論。

如果需要使用多個修飾符,只需要把它們並排列在結束分隔符之後即可,排列的順序是無關緊要的(注3)。請注意,斜線本身不是修飾符,你可以使用m/<title>/i、m|<title>|i,或是m{<title>}i,甚至是m<<title>>i。不過在講解修飾符時,通行的做法是加上一個斜線,例如「修飾符/i」。