內容

名稱

perlre - Perl 正規表示式

說明

此頁面說明 Perl 中正規表示式的語法。

如果您之前沒有使用過正規表示式,可以在 perlretut 中找到教學介紹。如果您對正規表示式略知一二,可以在 perlrequick 中找到快速入門介紹。

除了 "The Basics" 部分,本頁假設您熟悉正規表示式基礎,例如什麼是「樣式」、樣式看起來像什麼,以及如何基本使用樣式。有關如何使用正規表示式以及各種範例的參考,請參閱 "Regexp Quote-Like Operators" in perlop 中對 m//s///qr//"??" 的討論。

v5.22 新功能,use re 'strict' 在編譯正規表示式樣式時套用比平常更嚴格的規則。它可以找出一些雖然合法,但可能不是您預期的內容。

基礎

正規表示式是字串,其語法和意義非常特殊,說明見本文件和本文件引用的輔助文件。這些字串稱為「樣式」。樣式用來判斷另一個稱為「目標」的字串是否具有樣式所指定的特性(或不具有)。我們稱此為將目標字串與樣式「配對」。通常,配對是讓目標成為第一個運算元,而樣式成為 "Binding Operators" in perlop 中所列的兩個二元運算子 =~!~ 的第二個運算元;而樣式會透過 "Regexp Quote-Like Operators" in perlop 中的運算子之一從一般字串轉換而來,如下所示

$foo =~ m/abc/

如果且僅當變數 $foo 中的字串包含字元序列「a」、「b」,然後是「c」,則此表達式會評估為 true。(=~ m 或配對運算子說明見 "m/PATTERN/msixpodualngc" in perlop。)

尚未儲存在某些變數中的樣式必須在兩端以分隔字元分隔。這些分隔字元通常如上例所示,是正斜線,而文件中的樣式寫法通常也使用這些斜線。在大部分情況下,分隔字元前後都是同一個字元,但在少數情況中,一個字元看起來像是有鏡像對應字元,其中開啟版本是起始分隔字元,而關閉版本是結束分隔字元,例如

$foo =~ m<abc>

大多數時候,模式會在雙引號的語境中評估,但可以選擇分隔符號來強制使用單引號,例如

$foo =~ m'abc'

如果模式包含其分隔符號,則必須跳脫該分隔符號。在前面加上反斜線(例如"/foo\/bar/")即可達到此目的。

模式中的任何單一字元都與目標字串中的同一個字元相符,除非該字元是本文檔中描述具有特殊意義的元字元。一連串的非元字元與目標字串中的同一個字串相符,如我們在 m/abc/ 中看到的。

只有少數字元(全部都是 ASCII 標點符號字元)是元字元。最常用的字元是句點 ".",它通常與幾乎任何字元(包括句點本身)相符。

你可以讓通常作為元字元的字元透過在前面加上 "\" 來以字面意義解釋,就像如果模式的分隔符號也出現在模式中時必須跳脫一樣。因此,"\." 只與字面上的句點相符,而不是 "." 的一般意義。這表示反斜線也是一個元字元,所以 "\\" 與單一的 "\" 相符。而包含跳脫元字元的字串與目標字串中的同一個字串(但不包含跳脫)相符。所以,模式 /blur\\fl/ 會與包含字串 "blur\fl" 的任何目標字串相符。

元字元 "|" 用於與一個或另一個相符。因此

$foo =~ m/this|that/

僅當 $foo 包含字串 "this" 或字串 "that" 時才為 TRUE。與所有元字元一樣,在 "|" 前面加上反斜線會讓它與純粹的標點符號字元相符;在它的情況中,是垂直線。

$foo =~ m/this\|that/

僅當 $foo 包含字串 "this|that" 時才為 TRUE。

你不僅限於單一的 "|"

$foo =~ m/fee|fie|foe|fum/

僅當 $foo 包含兒童故事「傑克與魔豆」中的那 4 個字串中的任何一個時才為 TRUE。

如你所見,"|" 的結合力比一連串的普通字元低。我們可以使用分組元字元,括號 "("")" 來覆寫這個。

$foo =~ m/th(is|at) thing/

如果且僅當 $foo 包含序列 "this thing" 或序列 "that thing" 時,為 TRUE。與括號中模式部分相符的字串部分通常會個別提供,以便稍後在模式、替換或程式中使用。這稱為「擷取」,可能會很複雜。請參閱 "擷取群組"

第一個選項包含從最後一個模式分隔符號 ("(""(?:" (稍後說明)、或模式開頭) 到第一個 "|" 的所有內容,而最後一個選項包含從最後一個 "|" 到下一個關閉模式分隔符號的所有內容。這就是為什麼將選項包含在括號中是很常見的做法:為了減少對它們從哪裡開始和結束的混淆。

選項從左到右嘗試,因此找到的第一個選項(整個表達式與之相符),就是所選的選項。這表示選項不一定貪婪。例如:將 foo|foot"barefoot" 相符時,只有 "foo" 部分會相符,因為這是嘗試的第一個選項,而且它成功與目標字串相符。(這可能看起來不重要,但在使用括號擷取相符文字時,它很重要。)

除了取消元字元的特殊含義之外,加上反斜線前綴會將一些字母和數字字元從只與它們自己相符改為具有特殊含義。這些稱為「跳脫序列」,所有這些都說明於 perlrebackslash 中。字母或數字的跳脫序列(目前對 Perl 沒有特殊含義)會在啟用警告時產生警告,因為它們保留供未來潛在使用。

其中一個序列是 \b,它與某種類型的邊界相符。\b{wb} 和其他幾個提供特殊類型的邊界。(它們都從 perlrebackslash 中的「\b{}, \b, \B{}, \B」 開始詳細說明。)請注意,這些不與字元相符,而是與字元之間的零寬度空白相符。它們是 零寬度斷言 的一個範例。再考慮一下,

$foo =~ m/fee|fie|foe|fum/

除了這 4 個字之外,如果任何序列「feed」、「field」、「Defoe」、「fume」和許多其他序列在 $foo 中,它會評估為 TRUE。透過明智地使用 \b(或更好(因為它旨在處理自然語言)\b{wb}),我們可以確保只有巨人的話會相符

$foo =~ m/\b(fee|fie|foe|fum)\b/
$foo =~ m/\b{wb}(fee|fie|foe|fum)\b{wb}/

最後一個範例顯示字元 "{""}" 是元字元。

跳脫序列的另一種用途是指定無法(或您不想)以字面意義撰寫的字元。這些內容在 perlrebackslash 中的「字元跳脫」中有詳細說明,但接下來的三個段落會簡要說明其中一些內容。

各種控制字元可以用 C 語言樣式撰寫:"\n" 符合換行符號、"\t" 符合 tab 鍵、"\r" 符合回車符號、"\f" 符合換頁符號,等等

更一般來說,\nnn(其中 nnn 是三個八進位數字的字串)符合原生碼點為 nnn 的字元。如果您沒有三個數字,很容易遇到問題。因此,請務必使用三個數字,或者自 Perl 5.14 起,您可以使用 \o{...} 來指定任何數量的八進位數字。

類似地,\xnn(其中 nn 是十六進位數字)符合原生序數為 nn 的字元。同樣地,不使用兩個數字會造成災難,但您可以使用 \x{...} 來指定任何數量的十六進位數字。

除了是元字元之外,"." 是「字元類別」的範例,可以符合給定字元集中的任何單一字元。在這種情況下,該集幾乎是所有可能的字元。除了 "." 之外,Perl 還預先定義了幾個字元類別;有一個專門的參考頁面,perlrecharclass

您可以定義自己的自訂字元類別,方法是在模式中放入適當位置,列出您要在集中使用的所有字元。您可以透過在 [] 括號字元中封裝清單來執行此操作。當我們精確時,這些稱為「括號字元類別」,但通常會省略「括號」這個字。(省略它通常不會造成混淆。)這表示 "[" 字元是另一個元字元。它本身不符合任何內容;它僅用於告訴 Perl 其後面的內容是括號字元類別。如果您要符合左中括號字面值,您必須跳脫它,例如 "\["。匹配的 "]" 也是一個元字元;同樣地,它本身不符合任何內容,但只標記自訂類別的結尾。它是「有時元字元」的範例。如果沒有對應的 "[",它就不是元字元,並符合其字面值

print "]" =~ /]/;  # prints 1

字元類別內的字元清單提供該類別所配對的字元組。"[abc]" 會配對單一的 "a" 或 "b" 或 "c"。但是,如果 "[" 之後的第 1 個字元為 "^",該類別則會配對清單中沒有的任何字元。在清單中,"-" 字元會指定字元範圍,因此 a-z 代表 "a" 到 "z" 之間的所有字元(包含)。如果您要 "-""]" 本身成為類別的成員,請將它放在清單的開頭(可能在 "^" 之後),或使用反斜線進行跳脫。當 "-" 在清單的結尾,就在關閉 "]" 之前時,它也會被視為字面意思。(以下所有都指定相同的 3 個字元類別:[-az][az-][a\-z]。所有都不同於 [a-z],後者指定包含 26 個字元的類別,即使在基於 EBCDIC 的字元組中也是如此。)

括號字元類別還有更多內容;完整詳細資訊請參閱 "perlrecharclass 中的括號字元類別"

元字元

"基礎知識" 介紹了一些元字元。本節將說明所有元字元。它們大多數的意義與 egrep 指令中的意義相同。

只有 "\" 永遠是元字元。其他元字元有時才是元字元。下列表格列出所有元字元,摘要其用途,並說明它們是元字元的脈絡。在這些脈絡之外或在 "\" 之前,它們會配對其對應的標點符號字元。在某些情況下,它們的意義會根據各種模式修改器而有所不同,這些修改器會改變預設行為。請參閱 "修改器"

           PURPOSE                                  WHERE
\   Escape the next character                    Always, except when
                                                 escaped by another \
^   Match the beginning of the string            Not in []
      (or line, if /m is used)
^   Complement the [] class                      At the beginning of []
.   Match any single character except newline    Not in []
      (under /s, includes newline)
$   Match the end of the string                  Not in [], but can
      (or before newline at the end of the       mean interpolate a
      string; or before any newline if /m is     scalar
      used)
|   Alternation                                  Not in []
()  Grouping                                     Not in []
[   Start Bracketed Character class              Not in []
]   End Bracketed Character class                Only in [], and
                                                   not first
*   Matches the preceding element 0 or more      Not in []
      times
+   Matches the preceding element 1 or more      Not in []
      times
?   Matches the preceding element 0 or 1         Not in []
      times
{   Starts a sequence that gives number(s)       Not in []
      of times the preceding element can be
      matched
{   when following certain escape sequences
      starts a modifier to the meaning of the
      sequence
}   End sequence started by {
-   Indicates a range                            Only in [] interior
#   Beginning of comment, extends to line end    Only with /x modifier

請注意,大多數元字元在出現在括號字元類別中時會失去其特殊意義,但 "^" 在出現在此類別的開頭時具有不同的意義。而 "-""]" 僅在括號字元類別中的特定位置才是元字元;而 "}" 僅在關閉由 "{" 開始的特殊結構時才是元字元。

在雙引號脈絡中,通常情況下,您需要小心處理 "$" 和非元字元 "@"。這些可能會內插變數,這可能不是您想要的。

這些規則的設計考量在於表達的簡潔性,而不是可讀性和可維護性。"/x 和 /xx" 模式修改器允許您插入空白以提高可讀性。而使用 re 'strict' 會新增額外的檢查,以偵測一些可能會在不知不覺中編譯成非預期結果的拼寫錯誤。

預設情況下,保證 "^" 字元只會比對字串的開頭,"$" 字元只會比對結尾(或結尾前的新行),而 Perl 會針對假設字串只包含一行的狀況進行某些最佳化。內嵌新行不會被 "^""$" 比對到。不過,您可能希望將字串視為多行緩衝區,讓 "^" 會在字串內的任何新行之後比對(除非新行是字串中的最後一個字元),而 "$" 會在新行之前比對。您可以在比對模式運算子中使用 "/m" 修飾詞,以付出較小的效能代價來達成此目的。(較舊的程式會透過設定 $* 來執行此動作,但此選項已在 Perl 5.10 中移除。)

為了簡化多行取代,"." 字元永遠不會比對到新行,除非您使用 /s 修飾詞,這會讓 Perl 將字串視為單行,即使實際上並非如此。

修飾詞

概觀

可以使用各種修飾詞來變更比對的預設行為。與模式詮釋相關的修飾詞列在下方。變更 Perl 使用模式方式的修飾詞則詳述在 perlop 中的「類 Regexp 引用運算子」perlop 中的「引號建構的剖析細節」 中。可以動態新增修飾詞;請參閱下方的 「延伸模式」

m

將要比對的字串視為多行。也就是說,將 "^""$" 從比對字串第一行的開頭和最後一行的結尾變更為比對字串中每一行的開頭和結尾。

s

將字串視為單行。也就是說,將 "." 變更為比對任何字元,甚至包括新行,而這在一般情況下是不會比對到的。

同時使用這兩個修飾詞,例如 /ms,可以讓 "." 比對任何字元,同時仍讓 "^""$" 分別比對字串中新行之後和之前的字元。

i

執行不分大小寫的模式比對。例如,在 /i 下,「A」會比對到「a」。

如果區域設定比對規則生效,則字元對應會從目前區域設定中取得小於 255 的碼點,並從 Unicode 規則取得較大的碼點。不過,除非區域設定為 UTF-8,否則會跨越 Unicode 規則/非 Unicode 規則邊界 (序數 255/256) 的比對不會成功。請參閱 perllocale

有許多 Unicode 字元在 /i 下比對多個字元序列。例如,LATIN SMALL LIGATURE FI 應比對序列 fi。當多個字元在樣式中且分隔在群組之間,或當一個或多個被量化時,Perl 目前無法執行此動作。因此

"\N{LATIN SMALL LIGATURE FI}" =~ /fi/i;          # Matches
"\N{LATIN SMALL LIGATURE FI}" =~ /[fi][fi]/i;    # Doesn't match!
"\N{LATIN SMALL LIGATURE FI}" =~ /fi*/i;         # Doesn't match!

# The below doesn't match, and it isn't clear what $1 and $2 would
# be even if it did!!
"\N{LATIN SMALL LIGATURE FI}" =~ /(f)(i)/i;      # Doesn't match!

除非明確提到對應到它們的字元,否則 Perl 不會比對方括弧字元類別中的多個字元,且如果字元類別被反轉,Perl 根本不會比對它們,否則可能會造成極大的混淆。請參閱 「perlrecharclass 中的「方括弧字元類別」,以及 「perlrecharclass 中的「否定」

xxx

允許空白和註解以延伸樣式的可讀性。詳細資訊請參閱 「/x 和 /xx」

p

保留比對的字串,以便在比對後可以使用 ${^PREMATCH}${^MATCH}${^POSTMATCH}

在 Perl 5.20 和更高版本中,這會被忽略。由於新的寫入時複製機制,${^PREMATCH}${^MATCH}${^POSTMATCH} 會在比對後可用,與修改器無關。

adlu

這些修改器全部在 5.14 中新增,會影響使用哪些字元集規則 (Unicode 等),如下方 「字元集修改器」 中所述。

n

防止群組後設字元 () 擷取。此修改器在 5.22 中新增,會停止填入 $1$2 等。

"hello" =~ /(hi|hello)/;   # $1 is "hello"
"hello" =~ /(hi|hello)/n;  # $1 is undef

這等同於在每個擷取群組的開頭加上 ?:

"hello" =~ /(?:hi|hello)/; # $1 is undef

/n 可以針對每個群組進行否定。或者,仍可以使用命名擷取。

"hello" =~ /(?-n:(hi|hello))/n;   # $1 is "hello"
"hello" =~ /(?<greet>hi|hello)/n; # $1 is "hello", $+{greet} is
                                  # "hello"
其他修改器

在正規表示式結構的結尾可以找到許多標記,這些標記不是通用的正規表示式標記,而是套用於執行中的操作,例如比對或替換(分別為 m//s///)。

"perlretut 中使用 Perl 中的正規表示式" 中進一步說明的標記為

c  - keep the current position during repeated matching
g  - globally match the pattern repeatedly in the string

"perlop 中的 s/PATTERN/REPLACEMENT/msixpodualngcer" 中說明的替換特定修改器為

e  - evaluate the right-hand side as an expression
ee - evaluate the right side as a string then eval the result
o  - pretend to optimize your code, but actually introduce bugs
r  - perform non-destructive substitution and return the new value

正規表示式修改器通常在文件中寫成例如,「/x 修改器」,即使有問題的分隔符號可能實際上不是斜線。修改器 /imnsxadlup 也可以使用 (?...) 結構嵌入正規表示式本身,請參閱下方的 "延伸模式"

某些修改器的詳細資料

某些修改器需要比上面 "概觀" 中提供的更多說明。

/x/xx

單一的 /x 告訴正規表示式剖析器忽略大部分空白,這些空白既未反斜線化,也不在括號內的字元類別中,也不在多字元後設模式的字元中,例如 (?i: ... )。您可以使用此功能將正規表示式分解成更易於閱讀的部分。此外,"#" 字元會被視為後設字元,用於引入註解,直到模式的結尾分隔符號,或如果模式延伸到下一行,則直到目前的行的結尾。因此,這非常類似於一般的 Perl 程式碼註解。(您只能在註解中包含結尾分隔符號,前提是您在前面加上反斜線,所以請小心!)

使用 /x 表示如果您要在模式中使用真正的空白字元或 "#" 字元(在方括弧字元類別之外,不受 /x 影響),則您必須跳脫它們(使用反斜線或 \Q...\E)或使用八進制、十六進制或 \N{}\p{name=...} 跳脫來編碼它們。嘗試透過反斜線或 \Q 跳脫 \n 來將註解繼續到下一行是無效的。

您可以使用 "(?#text)" 來建立一個在當前行結束之前結束的註解,但 text 也不能包含結束分隔符號,除非使用反斜線跳脫。

一個常見的陷阱是忘記 "#" 字元(在方括弧字元類別之外)在 /x 下會開始一個註解,而不是以字面意義相符。當您嘗試找出為什麼特定 /x 模式無法如預期般運作時,請記住這一點。在方括弧字元類別中,"#" 保留其非特殊字面意義。

從 Perl v5.26 開始,如果修飾詞中有第二個 "x",則單一 /x 的效果會增加。唯一的差別是,在方括弧字元類別中,未跳脫(由反斜線)的 SPACE 和 TAB 字元不會新增到類別中,因此可以插入它們以使類別更易於閱讀

/ [d-e g-i 3-7]/xx
/[ ! @ " # $ % ^ & * () = ? <> ' ]/xx

可能比壓縮的等效項更容易理解

/[d-eg-i3-7]/
/[!@"#$%^&*()=?<>']/

請注意,這不幸地並不表示您的方括弧類別可以包含註解或延伸到多行。字元類別中的 # 仍然只是一個字面 #,不會引入註解。而且,除非結束方括弧與起始方括弧在同一行,否則換行字元(以及下一行直到 ] 結束的所有內容)將成為類別的一部分,就像您寫入 \n 一樣。

綜合來說,這些功能在提高 Perl 正規表示式的可讀性方面大有幫助。以下是範例

    # Delete (most) C comments.
    $program =~ s {
	/\*	# Match the opening delimiter.
	.*?	# Match a minimal number of characters.
	\*/	# Match the closing delimiter.
    } []gsx;

請注意,\Q...\E 內部的任何內容都不會受到 /x 的影響。請注意,/x 也不會影響單一多字元結構中的空白詮釋。例如,(?:...)"(""?"":" 之間不能有空白。在這種結構的任何分隔符號中,允許的空白不受 /x 影響,並且取決於結構。例如,所有使用大括號作為分隔符號的結構,例如 \x{...},可以在大括號內但緊鄰大括號處有空白,但不能在其他地方,也不能有非空白的空白字元。例外情況是遵循 Unicode 規則的 Unicode 屬性,請參閱 "perluniprops 中可透過 \p{} 和 \P{} 存取的屬性"

被視為空白的字元集是 Unicode 所稱的「樣式空白」,即

U+0009 CHARACTER TABULATION
U+000A LINE FEED
U+000B LINE TABULATION
U+000C FORM FEED
U+000D CARRIAGE RETURN
U+0020 SPACE
U+0085 NEXT LINE
U+200E LEFT-TO-RIGHT MARK
U+200F RIGHT-TO-LEFT MARK
U+2028 LINE SEPARATOR
U+2029 PARAGRAPH SEPARATOR

字元集修改器

/d/u/a/l(從 5.14 版開始提供)稱為字元集修改器;它們會影響用於正規表示式的字元集規則。

/d/u/l 修改器不太可能對您有很大幫助,因此您不必太擔心它們。它們存在於 Perl 的內部使用中,以便可以自動序列化複雜的正規表示式資料結構,並在稍後精確地重建它們,包括它們的所有細微差別。但是,由於 Perl 無法保守秘密,而且在某些罕見情況下它們可能會很有用,因此在此記錄它們。

另一方面,/a 修改器可能會很有用。它的目的是允許主要處理 ASCII 資料的程式碼不必關注 Unicode。

簡而言之,/l 將字元集設定為在執行樣式比對時有效的任何 Locale。

/u 將字元集設定為 Unicode。

/a 也將字元集設定為 Unicode,但增加了幾個針對 ASCII 安全比對的限制。

/d 是 5.14 版之前有問題的舊版 Default 字元集行為。它唯一的用途是強制使用舊版行為。

在任何給定的時間,這些修改器中只有一個會生效。它們的存在允許 Perl 保留正規表示式的原始編譯行為,無論在實際執行時有哪些規則生效。如果它內插到更大的正規表示式中,原始的規則將繼續適用於它,並且不會影響其他部分。

在各種實用規則的範圍內編譯正規表示式時,會自動選取 /l/u 修飾詞,我們建議您通常使用這些實用規則,而不是明確指定這些修飾詞。首先,修飾詞只會影響模式比對,甚至不會延伸到任何執行的替換,而使用實用規則會在它們的範圍內為所有適當的運算提供一致的結果。例如,

s/foo/\Ubar/il

會使用區域設定的規則比對大小寫不敏感的「foo」,但 /l 不會影響 \U 的運作方式。您很可能希望它們兩個都使用區域設定規則。若要執行此動作,請在 use locale 的範圍內編譯正規表示式。這會同時隱式加入 /l,並將區域設定規則套用至 \U。教訓是使用 use locale,而不是明確使用 /l

類似地,最好使用 use feature 'unicode_strings',而不是

s/foo/\Lbar/iu

來取得 Unicode 規則,因為前者的 \L(但不一定是後者)也會使用 Unicode 規則。

以下是每個修飾詞的更多詳細資料。您很可能不需要知道 /l/u/d 的這些詳細資料,可以跳到 /a

/l

表示在模式比對時使用目前的區域設定規則(請參閱 perllocale)。例如,\w 會比對該區域設定的「字元」,而 "/i" 大小寫不敏感的比對會根據區域設定的字母大小寫轉換規則進行比對。使用的區域設定會是在執行模式比對時生效的區域設定。這可能與編譯時期的區域設定不同,如果在期間呼叫了 setlocale() 函數,則可能與一個比對不同於另一個比對。

在 v5.20 之前,Perl 不支援多位元組區域設定。從那時開始,支援 UTF-8 區域設定。永遠不太可能支援其他多位元組區域設定。然而,在所有區域設定中,您都可以有超過 255 的碼點,而且這些碼點會始終視為 Unicode,無論目前生效的區域設定為何。

在 Unicode 規則下,有少數幾個跨越 255/256 邊界的、大小寫不敏感的比對。除了 Perl v5.20 和更新版本中的 UTF-8 區域設定之外,這些比對在 /l 下是不允許的。例如,0xFF(在 ASCII 平台上)不會與碼點為 0x178 的字元 LATIN CAPITAL LETTER Y WITH DIAERESIS 進行大小寫不敏感的比對,因為 0xFF 在目前的區域設定中可能不是 LATIN SMALL LETTER Y WITH DIAERESIS,而且 Perl 沒有辦法知道該字元是否甚至存在於區域設定中,更不用說它是什麼碼點了。

在 v5.20 和後續版本中的 UTF-8 區域設定中,區域設定和非區域設定在正規表示式中唯一可見的差異應該是污染,如果您的 perl 支援污染檢查(請參閱 perlsec)。

此修飾符可以指定為預設值,方法是 use locale,但請參閱 "哪個字元集修飾符有效?"

/u

表示在模式配對時使用 Unicode 規則。在 ASCII 平台上,這表示 128 到 255 之間的代碼點採用其 Latin-1 (ISO-8859-1) 含義(與 Unicode 的相同)。(否則 Perl 會認為它們的含義未定義。)因此,在此修飾符下,ASCII 平台實際上會變成 Unicode 平台;因此,例如,\w 會配對 Unicode 中超過 100,000 個字元字元中的任何一個。

與大多數特定於語言和國家/地區配對的區域設定不同,Unicode 將世界上某處作為字母的所有字元分類為 \w。例如,您的區域設定可能不會認為 LATIN SMALL LETTER ETH 是字母(除非您碰巧會講冰島語),但 Unicode 會。類似地,世界上某處的所有十進位數字字元都會配對 \d;這是數百個,而不是 10 個可能的配對。其中一些數字看起來像某些 10 個 ASCII 數字,但表示不同的數字,因此人類很容易認為數字與實際數字不同。例如,BENGALI DIGIT FOUR (U+09EA) 看起來非常像 ASCII DIGIT EIGHT (U+0038),而 LEPCHA DIGIT SIX (U+1C46) 看起來非常像 ASCII DIGIT FIVE (U+0035)。而且,\d+ 可能會配對來自不同書寫系統的數字字串,造成安全問題。例如,詐騙網站可以使用 U+1C46 顯示某物的價格,而使用者會認為某物的價格是 500 個單位,但實際上是 600 個單位。強制執行腳本運行的瀏覽器("腳本執行")會防止這種詐騙顯示。Unicode::UCD 中的 "num()" 也可以用來解決這個問題。或者,/a 修飾符可用於強制 \d 僅配對 ASCII 0 到 9。

此外,在此修飾詞下,不分大小寫的比對會作用在整組 Unicode 字元。例如,KELVIN SIGN 會比對字母「k」和「K」;而 LATIN SMALL LIGATURE FF 會比對字串「ff」,如果你沒有準備好,可能會讓它看起來像十六進位常數,呈現另一個潛在的安全問題。請參閱 https://unicode.org/reports/tr36,以詳細了解 Unicode 安全問題。

此修飾詞可指定為預設值,方法是 use feature 'unicode_stringsuse locale ':not_characters'use v5.12(或更高版本),但請參閱 "哪個字元集修飾詞有作用?"

/d

重要:由於此修飾詞會造成無法預測的行為,因此僅在需要維持奇怪的向後相容性時才使用。在新程式碼中使用 unicode_strings 功能,以避免無意中預設啟用此修飾詞。

此修飾詞的作用是什麼?「看情況」!

此修飾詞表示使用平台原生比對規則,但當有原因改用 Unicode 規則時除外,如下所示

  1. 目標字串的 UTF8 標記(見下方)已設定;或

  2. 模式的 UTF8 標記(見下方)已設定;或

  3. 模式明確提到一個大於 255 的碼點(例如 \x{100});或

  4. 模式使用 Unicode 名稱(\N{...});或

  5. 模式使用 Unicode 屬性(\p{...}\P{...});或

  6. 模式使用 Unicode 斷行(\b{...}\B{...});或

  7. 模式使用 "(?[ ])"

  8. 模式使用 (*script_run: ...)

關於上述的「UTF8 標記」參照:正常情況下,Perl 應用程式不應該考慮該標記。它是 Perl 內部的一部分,因此 Perl 隨時都可以變更。因此,/d 可能會造成無法預測的結果。請參閱 perlunicode 中的 "perlunicode 中的「Unicode 錯誤」"。這個錯誤已經變得相當臭名昭著,導致這個修飾詞出現其他(沒有髒話)名稱,例如「Dicey」和「Dodgy」。

以下是說明如何在 ASCII 平台上運作的一些範例

$str =  "\xDF";        #
utf8::downgrade($str); # $str is not UTF8-flagged.
$str =~ /^\w/;         # No match, since no UTF8 flag.

$str .= "\x{0e0b}";    # Now $str is UTF8-flagged.
$str =~ /^\w/;         # Match! $str is now UTF8-flagged.
chop $str;
$str =~ /^\w/;         # Still a match! $str retains its UTF8 flag.

在 Perl 的預設組態中,當其他修飾詞都沒有選取時,此修飾詞會自動選取為預設,因此它還有一個名稱(很不幸地)是「預設」。

只要有可能,請使用 unicode_strings 來讓它變成預設。

/a(和 /aa)

此修飾詞代表 ASCII 限制(或 ASCII 安全)。此修飾詞可以加倍使用以增加其效果。

當它單獨出現時,它會讓序列 \d\s\w 和 Posix 字元類別只會在 ASCII 範圍內相符。因此它們會還原為 5.6 版以前、Unicode 以前的意思。在 /a 下,\d 永遠只表示數字 "0""9"\s 表示五個字元 [ \f\n\r\t],從 Perl v5.18 開始,還包括垂直定位標籤;\w 表示 63 個字元 [A-Za-z0-9_];同樣地,所有 Posix 類別,例如 [[:print:]],只會相符於適當的 ASCII 範圍字元。

此修飾詞對於只偶爾使用 Unicode 且不希望被它的複雜性和安全性問題所困擾的人來說很有用。

有了 /a,你可以放心地撰寫 \d,因為它只會相符於 ASCII 字元,而且如果需要相符於 ASCII 以外的字元,你可以改用 \p{Digit}(或 \p{Word} 取代 \w)。有類似的 \p{...} 結構可以相符於 ASCII 以外的空白(請參閱 "Whitespace" in perlrecharclass)和 Posix 類別(請參閱 "POSIX Character Classes" in perlrecharclass)。因此,此修飾詞並不表示你不能使用 Unicode,而是表示你必須明確使用一個表示 Unicode 的結構(\p{}\P{})才能取得 Unicode 相符。

正如你所預期的,此修飾詞會讓 \D 表示與 [^0-9] 相同的東西;事實上,所有非 ASCII 字元都會相符於 \D\S\W\b 仍然表示相符於 \w\W 之間的邊界,使用它們的 /a 定義(\B 也是如此)。

否則,/a 會像 /u 修飾詞一樣運作,因為不區分大小寫的相符使用 Unicode 規則;例如,「k」會在 /i 相符下相符於 Unicode \N{KELVIN SIGN},而且拉丁文 1 範圍內的碼點(高於 ASCII)在不區分大小寫的相符時會有 Unicode 規則。

若要禁止 ASCII/非 ASCII 相符(例如「k」和 \N{KELVIN SIGN}),請指定 "a" 兩次,例如 /aai/aia。("a" 的第一次出現會限制 \d 等,第二次出現會新增 /i 限制。)但是,請注意 ASCII 範圍以外的碼點會使用 Unicode 規則進行 /i 相符,因此此修飾詞並不會真正將所有事情限制在 ASCII 中;它只是禁止混合 ASCII 和非 ASCII。

總之,此修飾詞為不希望接觸到所有 Unicode 的應用程式提供保護。指定它兩次會提供額外的保護。

此修改器可透過 use re '/a'use re '/aa' 指定為預設值。如果您這麼做,您實際上可能偶爾需要明確使用 /u 修改器,如果有一些常規表示式您確實想要完整的 Unicode 規則(但即使在這裡,最好將所有內容都放在功能 "unicode_strings" 下,以及 use re '/aa')。另請參閱 "哪個字元集修改器有效?"

哪個字元集修改器有效?

在常規表示式中的任何給定點,這些修改器中的哪一個有效取決於一組相當複雜的互動。這些互動的設計目的是讓您通常不必擔心它,但本節提供血腥的細節。如下文 "延伸模式" 中所述,可以明確指定僅適用於常規表示式部分的修改器。最內層始終優先於任何外層,而適用於整個表示式的優先於本節其餘部分中描述的任何預設設定。

use re '/foo' 語法可用於設定其範圍內編譯的常規表示式的預設修改器(包括這些)。此語法優先於以下列出的其他語法,這些語法也會變更預設值。

否則,use locale 會將預設修改器設定為 /l;而 use feature 'unicode_stringsuse v5.12(或更高版本)在與 use localeuse bytes 不在同一個範圍內時,將預設值設定為 /u。(use locale ':not_characters' 也會將預設值設定為 /u,覆寫任何純粹的 use locale。)與上述機制不同,這些機制會影響常規表示式模式配對以外的操作,因此與其他運算子提供更一致的結果,包括在替換中使用 \U\l 等。

如果上述情況均不適用,則基於向後相容性的原因,/d 修改器是預設有效的修改器。由於這可能會導致意外的結果,最好指定應使用哪個其他規則集。

Perl 5.14 之前的字元集修改器行為

5.14 之前,沒有明確的修改器,但 /l 暗示在 use locale 範圍內編譯的正規表示式,而 /d 則暗示其他情況。然而,將正規表示式內插到較大的正規表示式中會忽略原始編譯,而偏好第二次編譯時生效的內容。/d 修改器有許多不一致(錯誤),在不適當的時候會使用 Unicode 規則,反之亦然。\p{} 沒有暗示 Unicode 規則,直到 5.12,\N{} 的所有出現也沒有暗示。

正規表示式

量詞

當模式的特定部分需要匹配特定次數(或數字)時,會使用量詞。如果沒有量詞,則匹配的次數恰好為一次。識別下列標準量詞

*           Match 0 or more times
+           Match 1 or more times
?           Match 1 or 0 times
{n}         Match exactly n times
{n,}        Match at least n times
{,n}        Match at most n times
{n,m}       Match at least n but not more than m times

(如果非跳脫的大括號出現在上述量詞以外的內容中,且不構成反斜線序列的一部分,例如 \x{...},則會產生致命語法錯誤,或視為一般字元處理,通常會產生不建議使用的警告。若要跳脫,可以在前面加上反斜線 ("\{") 或將其括在方括號中 ("[{]")。此變更將允許未來的語法擴充(例如使量詞的下限為選用),並能更佳地檢查量詞的錯誤)。

"*" 量詞等於 {0,}"+" 量詞等於 {1,}"?" 量詞等於 {0,1}nm 限制為小於 perl 建置時定義的預設限制的非負整數值。在最常見的平台上,這通常是 65534。可以在此類程式碼產生的錯誤訊息中看到實際限制

$_ **= $_ , / {$_} / for 2 .. 42;

預設情況下,量化的子模式是「貪婪的」,也就是說,它會在允許模式的其餘部分匹配的同時,盡可能多地匹配(給定特定的起始位置)。如果您希望它匹配最少次數,請在量詞後面加上 "?"。請注意,意義不會改變,只有「貪婪」會改變

*?        Match 0 or more times, not greedily
+?        Match 1 or more times, not greedily
??        Match 0 or 1 time, not greedily
{n}?      Match exactly n times, not greedily (redundant)
{n,}?     Match at least n times, not greedily
{,n}?     Match at most n times, not greedily
{n,m}?    Match at least n but not more than m times, not greedily

通常,當量化的子模式不允許模式的其餘部分匹配時,Perl 會回溯。然而,這種行為有時是不希望的。因此,Perl 也提供了「佔有」量詞形式。

*+     Match 0 or more times and give nothing back
++     Match 1 or more times and give nothing back
?+     Match 0 or 1 time and give nothing back
{n}+   Match exactly n times and give nothing back (redundant)
{n,}+  Match at least n times and give nothing back
{,n}+  Match at most n times and give nothing back
{n,m}+ Match at least n but not more than m times and give nothing back

例如,

'aaaa' =~ /a++a/

永遠不會匹配,因為 a++ 會吞噬字串中的所有 "a",而不會為模式的其餘部分留下任何 "a"。此功能非常有用,可以提供 perl 關於不應回溯的位置的提示。例如,典型的「匹配雙引號字串」問題在寫成

/"(?:[^"\\]++|\\.)*+"/

時,可以最有效率地執行,因為我們知道如果最後一個引號不匹配,回溯將無法提供幫助。請參閱獨立子表示式 "(?>pattern)" 以取得更多詳細資訊;佔有量詞只是該結構的語法糖。例如,上述範例也可以寫成

/"(?>(?:(?>[^"\\]+)|\\.)*)"/

請注意,佔有量詞修改器不能與非貪婪修改器結合。這是因為這沒有意義。請考慮下列等價表

Illegal         Legal
------------    ------
X??+            X{0}
X+?+            X{1}
X{min,max}?+    X{min}

跳脫序列

由於樣式被視為雙引號字串處理,以下內容也適用

\t          tab                   (HT, TAB)
\n          newline               (LF, NL)
\r          return                (CR)
\f          form feed             (FF)
\a          alarm (bell)          (BEL)
\e          escape (think troff)  (ESC)
\cK         control char          (example: VT)
\x{}, \x00  character whose ordinal is the given hexadecimal number
\N{name}    named Unicode character or character sequence
\N{U+263D}  Unicode character     (example: FIRST QUARTER MOON)
\o{}, \000  character whose ordinal is the given octal number
\l          lowercase next char (think vi)
\u          uppercase next char (think vi)
\L          lowercase until \E (think vi)
\U          uppercase until \E (think vi)
\Q          quote (disable) pattern metacharacters until \E
\E          end either case modification or quoted section, think vi

詳細資訊請參閱 perlop 中的「引號和類引號運算子」

字元類別和其他特殊跳脫

此外,Perl 定義下列內容

Sequence   Note    Description
 [...]     [1]  Match a character according to the rules of the
                  bracketed character class defined by the "...".
                  Example: [a-z] matches "a" or "b" or "c" ... or "z"
 [[:...:]] [2]  Match a character according to the rules of the POSIX
                  character class "..." within the outer bracketed
                  character class.  Example: [[:upper:]] matches any
                  uppercase character.
 (?[...])  [8]  Extended bracketed character class
 \w        [3]  Match a "word" character (alphanumeric plus "_", plus
                  other connector punctuation chars plus Unicode
                  marks)
 \W        [3]  Match a non-"word" character
 \s        [3]  Match a whitespace character
 \S        [3]  Match a non-whitespace character
 \d        [3]  Match a decimal digit character
 \D        [3]  Match a non-digit character
 \pP       [3]  Match P, named property.  Use \p{Prop} for longer names
 \PP       [3]  Match non-P
 \X        [4]  Match Unicode "eXtended grapheme cluster"
 \1        [5]  Backreference to a specific capture group or buffer.
                  '1' may actually be any positive integer.
 \g1       [5]  Backreference to a specific or previous group,
 \g{-1}    [5]  The number may be negative indicating a relative
                  previous group and may optionally be wrapped in
                  curly brackets for safer parsing.
 \g{name}  [5]  Named backreference
 \k<name>  [5]  Named backreference
 \k'name'  [5]  Named backreference
 \k{name}  [5]  Named backreference
 \K        [6]  Keep the stuff left of the \K, don't include it in $&
 \N        [7]  Any character but \n.  Not affected by /s modifier
 \v        [3]  Vertical whitespace
 \V        [3]  Not vertical whitespace
 \h        [3]  Horizontal whitespace
 \H        [3]  Not horizontal whitespace
 \R        [4]  Linebreak
[1]

請參閱 perlrecharclass 中的「方括號字元類別」 以取得詳細資訊。

[2]

請參閱 perlrecharclass 中的「POSIX 字元類別」 以取得詳細資訊。

[3]

請參閱 perlunicode 中的「Unicode 字元屬性」 以取得詳細資訊

[4]

請參閱 perlrebackslash 中的「雜項」 以取得詳細資訊。

[5]

請參閱下方 「擷取群組」 以取得詳細資訊。

[6]

請參閱下方 「延伸樣式」 以取得詳細資訊。

[7]

請注意,\N 有兩個意思。當為 \N{NAME} 格式時,它會比對名稱為 NAME 的字元或字元序列;同樣地,當為 \N{U+hex} 格式時,它會比對 Unicode 碼點為 hex 的字元。否則,它會比對任何字元,但排除 \n

[8]

請參閱 perlrecharclass 中的「延伸方括號字元類別」 以取得詳細資訊。

斷言

除了 "^""$" 之外,Perl 定義下列零寬度斷言

\b{}   Match at Unicode boundary of specified type
\B{}   Match where corresponding \b{} doesn't match
\b     Match a \w\W or \W\w boundary
\B     Match except at a \w\W or \W\w boundary
\A     Match only at beginning of string
\Z     Match only at end of string, or before newline at the end
\z     Match only at end of string
\G     Match only at pos() (e.g. at the end-of-match position
       of prior m//g)

Unicode 邊界 (\b{}),從 v5.22 開始提供,是兩個字元之間、字串中第一個字元之前或字串中最後一個字元之後的點,在該點上符合 Unicode 定義的特定條件。請參閱 perlrebackslash 中的「\b{}, \b, \B{}, \B」 以取得詳細資訊。

字詞邊界 (\b) 是兩個字元之間的點,其中一側有 \w,另一側有 \W (順序不拘),將字串開頭和結尾的假想字元視為與 \W 相符。(在字元類別中,\b 代表退格鍵,而不是字詞邊界,就像它在任何雙引號字串中通常所代表的一樣。) \A\Z 就像 "^""$",但當使用 /m 修飾詞時,它們不會比對多次,而 "^""$" 會在每個內部換行符號邊界比對。若要比對字串的實際結尾,而不忽略選擇性的尾端換行符號,請使用 \z

\G 斷言可串連全域比對(使用 m//g),如 perlop 中的「Regexp Quote-Like Operators」 所述。當撰寫類似 lex 的掃描器時,它也很有用,此時您有數個模式要比對字串中連續的子字串;請參閱前述參考。\G 的實際比對位置也可以透過將 pos() 用作左值來影響:請參閱 perlfunc 中的「pos」。請注意,零長度比對的規則(請參閱 「Repeated Patterns Matching a Zero-length Substring」)會稍微修改,在決定比對長度時,\G 左側的內容不會計算在內。因此,下列內容不會永遠比對

my $string = 'ABC';
pos($string) = 1;
while ($string =~ /(.\G)/g) {
    print $1;
}

它會印出「A」,然後終止,因為它將比對視為零寬度,因此不會在同一個位置比對兩次。

值得注意的是,\G 使用不當可能會導致無限迴圈。在交替中使用包含 \G 的模式時,請小心。

另請注意,s/// 會拒絕覆寫已取代的取代部分;因此,例如,這會在第一次反覆運算後停止,而不是從字串中向後反覆運算

$_ = "123456789";
pos = 6;
s/.(?=.\G)/X/g;
print; 	# prints 1234X6789, not XXXXX6789

擷取群組

群組建構 ( ... ) 會建立擷取群組(也稱為擷取緩衝區)。若要稍後在同一個模式中參照群組的目前內容,請使用 \g1(或 \g{1})表示第一個,\g2(或 \g{2})表示第二個,依此類推。這稱為反向參照。您可以使用的擷取子字串數量沒有限制。群組會編號,最左邊的開啟括號為編號 1,等等。如果群組沒有比對,則相關的反向參照也不會比對。(如果群組是選用的,或在交替的不同分支中,就會發生這種情況。)您可以省略 "g",並寫入 "\1"等等,但此表單有一些問題,如下所述。

您也可以使用負數來相對應地參照擷取群組,因此 \g-1\g{-1} 都參照緊鄰在前的擷取群組,而 \g-2\g{-2} 則參照其前一個群組。例如

/
 (Y)            # group 1
 (              # group 2
    (X)         # group 3
    \g{-1}      # backref to group 3
    \g{-3}      # backref to group 1
 )
/x

將與 /(Y) ( (X) \g3 \g1 )/x 相同。這讓您得以將正規表示式內插到較大的正規表示式中,而且不必擔心擷取群組會重新編號。

您可以完全捨棄數字,並建立命名擷取群組。宣告的表示法為 (?<name>...),而參照的表示法為 \g{name}。(為了與 .Net 正規表示式相容,\g{name} 也可寫成 \k{name}\k<name>\k'name'。)name 不可以數字開頭,也不可包含連字號。當同一個模式中的不同群組具有相同名稱時,任何對該名稱的參照都會假設為最左邊已定義的群組。命名群組會計入絕對和相對編號,因此也可以透過這些數字來參照。(使用命名擷取群組可以執行一些事情,否則需要 (??{})。)

擷取群組內容具有動態範圍,而且在模式外對您來說可用,直到封閉區塊結束或在同一個範圍中下一個成功的比對(以先發生的為準)。請參閱 perlsyn 中的「複合陳述式」perlvar 中的「正規表示式變數的範圍規則」 以取得更多詳細資料。

您可以使用絕對數字(使用 "$1" 取代 "\g1" 等)或透過 %+ hash 使用名稱來存取擷取群組的內容,方法是使用 "$+{name}"

在參照命名擷取群組時需要大括號,但對於絕對或相對編號的擷取群組來說則為選用。在透過串接較小的字串來建立正規表示式時,大括號較為安全。例如,如果您有 qr/$a$b/,而 $a 包含 "\g1",而 $b 包含 "37",您將會得到 /\g137/,這可能不是您想要的。

如果您使用大括號,您也可以選擇在括號內但緊鄰括號處加入任意數量的空白(空格或 tab)字元,例如 \g{ -1 }\k{ name }

\g\k 符號是在 Perl 5.10.0 中引入的。在此之前,沒有命名或相對編號的擷取群組。絕對編號的群組使用 \1\2 來參照,而且此符號仍然被接受(而且可能永遠都會被接受)。但是,如果有多於 9 個擷取群組,這會導致一些歧義,因為 \10 可能表示第十個擷取群組,或八進制中序數為 010 的字元(在 ASCII 中為退格鍵)。Perl 會透過在 \10 之前開啟至少 10 個左括號來解決此歧義。同樣地,\11 僅在 \11 之前開啟至少 11 個左括號時才是反向參照。依此類推。\1\9 永遠都解釋為反向參照。以下有幾個範例說明這些危險。如果您是指擷取群組,您可以透過永遠使用 \g{}\g 來避免歧義;而對於八進制常數,永遠使用 \o{},或對於 \077 及以下,使用 3 個以前導零填充的數字,因為前導零表示八進制常數。

\數字 符號在模式外的特定情況下也能運作。有關詳細資訊,請參閱下方的 "關於使用 \1 而不是 $1 的警告"

範例

s/^([^ ]*) *([^ ]*)/$2 $1/;     # swap first two words

/(.)\g1/                        # find first doubled char
     and print "'$1' is the first doubled character\n";

/(?<char>.)\k<char>/            # ... a different way
     and print "'$+{char}' is the first doubled character\n";

/(?'char'.)\g1/                 # ... mix and match
     and print "'$1' is the first doubled character\n";

if (/Time: (..):(..):(..)/) {   # parse out values
    $hours = $1;
    $minutes = $2;
    $seconds = $3;
}

/(.)(.)(.)(.)(.)(.)(.)(.)(.)\g10/   # \g10 is a backreference
/(.)(.)(.)(.)(.)(.)(.)(.)(.)\10/    # \10 is octal
/((.)(.)(.)(.)(.)(.)(.)(.)(.))\10/  # \10 is a backreference
/((.)(.)(.)(.)(.)(.)(.)(.)(.))\010/ # \010 is octal

$a = '(.)\1';        # Creates problems when concatenated.
$b = '(.)\g{1}';     # Avoids the problems.
"aa" =~ /${a}/;      # True
"aa" =~ /${b}/;      # True
"aa0" =~ /${a}0/;    # False!
"aa0" =~ /${b}0/;    # True
"aa\x08" =~ /${a}0/;  # True!
"aa\x08" =~ /${b}0/;  # False

幾個特殊變數也會回溯到前一個比對的部分。$+ 會傳回最後一個方括號比對比對到的內容。$& 會傳回整個比對到的字串。(在某個時間點,$0 也會傳回,但現在它會傳回程式名稱。)$` 會傳回比對到的字串之前的所有內容。$' 會傳回比對到的字串之後的所有內容。而 $^N 會包含最近關閉的群組(子比對)比對到的內容。$^N 可以用在延伸模式中(請參閱下方),例如將子比對指定給變數。

這些特殊變數(例如 %+ hash 和編號比對變數($1$2$3))是動態範圍,直到封閉區塊結束或下一個成功的比對(以先發生的為準)。(請參閱 perlsyn 中的 "複合陳述式"。)

@{^CAPTURE} 陣列可以用來存取所有擷取緩衝區,作為陣列,而不需要知道有多少個擷取緩衝區。例如

$string=~/$pattern/ and @captured = @{^CAPTURE};

會將每個擷取變數($1$2 等)的複本放入 @captured 陣列中。

請注意,當內插 @{^CAPTURE} 陣列的腳本時,您必須使用分隔的大括號符號

print "@{^CAPTURE[0]}";

請參閱 perldata 中的 "使用大括號的分隔變數名稱" 以取得有關此符號的更多資訊。

注意:Perl 中的失敗比對不會重設比對變數,這使得撰寫測試一系列更特定案例並記住最佳比對的程式碼變得更容易。

警告:如果您的程式碼要在 Perl 5.16 或更早版本上執行,請注意,一旦 Perl 發現您在程式中任何地方需要 $&$`$' 之一,它就必須為每個模式比對提供它們。這可能會大幅降低程式的速度。

Perl 使用相同的機制來產生 $1$2 等,因此您也必須為每個包含擷取括弧的模式付出代價。(若要避免這種代價,同時保留群組行為,請改用延伸正規表示法 (?: ... )。)但如果您從未使用過 $&$`$',則沒有擷取括弧的模式不會受到懲罰。因此,如果您能避免使用 $&$'$`,請盡量避免,但如果您不能(而且有些演算法真的需要它們),一旦您使用過一次,請隨意使用,因為您已經付出了代價。

Perl 5.16 導入了一個稍微更有效率的機制,分別記錄是否已看到 $`$&$',因此可能只需要複製字串的一部分。Perl 5.20 導入了一個更有效率的寫入時複製機制,消除了任何減速。

作為解決此問題的另一種方法,Perl 5.10.0 導入了 ${^PREMATCH}${^MATCH}${^POSTMATCH},它們等於 $`$&$'它們僅保證在使用 /p(保留)修飾詞執行的成功比對後定義。與其標點符號字元等效項不同,使用這些變數不會產生整體效能損失,但折衷的是您必須在想要使用它們時告訴 Perl。從 Perl 5.20 開始,這三個變數等於 $`$&$',且忽略 /p

引用元字元

Perl 中的轉義元字元為字母數字,例如 \b\w\n。與其他正規表示式語言不同,沒有非字母數字的轉義符號。因此,任何看起來像 \\\(\)\[\]\{\} 的字元總是會被解釋為字面字元,而非元字元。這曾用於常見的慣用語法中,以停用或引用您想要用於模式的字串中正規表示式元字元的特殊含義。只需引用所有非「字詞」字元

$pattern =~ s/(\W)/\\$1/g;

(如果設定 use locale,則這取決於目前的語言環境。)現在更常使用 quotemeta() 函式或 \Q 元引用跳脫序列來停用所有元字元的特殊含義,如下所示

/$unquoted\Q$quoted\E$unquoted/

請注意,如果您將字面轉義符號(不在內插變數中)放在 \Q\E 之間,雙引號轉義符號內插可能會導致令人困惑的結果。如果您需要\Q...\E 中使用字面轉義符號,請參閱 "perlop 中引號建構的解析血腥細節"

quotemeta()\Q"perlfunc 中的 quotemeta" 中有完整說明。

延伸模式

Perl 也定義了一致的延伸語法,用於在標準工具(例如 awklex)中找不到的功能。其中大多數的語法是在括號中,並以問號作為括號中的第一個項目。問號後的字元表示延伸。

選擇問號作為此項和最小匹配建構,原因有 1) 問號在較舊的正規表示式中很少見,以及 2) 每當您看到問號時,您都應該停下來「質疑」正在發生什麼事。這是心理學....

(?#text)

註解。文字 會被忽略。請注意,Perl 會在看到 ")" 時立即關閉註解,因此無法在註解中放入字面 ")"。如果模式的結尾分隔符號出現在註解中,則必須使用反斜線轉義。

請參閱 "/x",了解在模式中加入註解的另一種方法。

請注意,註解幾乎可以在任何地方使用,但跳脫序列中間除外。範例

qr/foo(?#comment)bar/'  # Matches 'foobar'

# The pattern below matches 'abcd', 'abccd', or 'abcccd'
qr/abc(?#comment between literal and its quantifier){1,3}d/

# The pattern below generates a syntax error, because the '\p' must
# be followed immediately by a '{'.
qr/\p(?#comment between \p and its property name){Any}/

# The pattern below generates a syntax error, because the initial
# '\(' is a literal opening parenthesis, and so there is nothing
# for the  closing ')' to match
qr/\(?#the backslash means this isn't a comment)p{Any}/

# Comments can be used to fold long patterns into multiple lines
qr/First part of a long regex(?#
  )remaining part/
(?adlupimnsx-imnsx)
(?^alupimnsx)

零個或多個內嵌的模式比對修飾詞,用於開啟(或在前面加上 "-" 時關閉)模式的剩餘部分或封閉模式群組的剩餘部分(如果有的話)。

這對於動態產生的模式特別有用,例如從組態檔中讀取、從引數中取得或在某個表格中指定的模式。考慮某些模式希望區分大小寫而某些模式不希望區分大小寫的情況:不區分大小寫的模式只需在模式的前面包含 (?i) 即可。例如

$pattern = "foobar";
if ( /$pattern/i ) { }

# more flexible:

$pattern = "(?i)foobar";
if ( /$pattern/ ) { }

這些修飾詞會在封閉群組的結尾處還原。例如,

( (?i) blah ) \s+ \g1

將比對任何大小寫的 blah、一些空白,以及前一個字詞的精確(包括大小寫!)重複,假設使用 /x 修飾詞,且在此群組外沒有 /i 修飾詞。

這些修飾詞不會傳遞到封閉群組中呼叫的名稱子模式。換句話說,例如 ((?i)(?&NAME)) 的模式不會變更 NAME 模式的大小寫敏感度。

修飾詞會被同一個範圍內後續出現的相同修飾詞建構覆寫,因此

/((?im)foo(?-m)bar)/

會不區分大小寫地比對所有 foobar,但僅對 foo 部分使用 /m 規則。"a" 旗標也會覆寫 aa;同樣地,aa 會覆寫 "a""x"xx 也是如此。因此,在

/(?-x)foo/xx

比對 foo 時,/x/xx 都會關閉。而在

/(?x)foo/x

比對 foo 時,/x 會開啟,但 /xx 卻不會。(人們可能會錯誤地認為,由於內部的 (?x) 已經在 /x 的範圍內,因此結果將有效地是它們的總和,產生 /xx。它並非這樣運作。)類似地,執行類似 (?xx-x)foo 的動作會關閉比對 foo 的所有 "x" 行為,而不是從 2 中減去 1 個 "x" 以得到剩餘的 1 個 "x"

這些修飾詞中的任何一個都可以設定為在 use re 範圍內編譯的所有正規表示式中全球套用。請參閱 "'/flags' 模式" in re

從 Perl 5.14 開始,"^"(插入符號或揚抑符)緊接在 "?" 之後,為 d-imnsx 的簡寫等效符號。旗標(除了 "d")可以出現在插入符號之後,以覆寫它。但它不能與減號一起使用。

請注意,"a""d""l""p""u" 修飾詞很特別,因為它們只能啟用,不能停用,而且 "a""d""l""u" 修飾詞是互斥的:指定一個會取消指定其他修飾詞,而且結構中最多只能出現一個(或兩個 "a")。因此,例如,在 use warnings 下編譯時,(?-p) 會發出警告;(?-d:...)(?dl:...) 是致命錯誤。

另外請注意,"p" 修飾詞很特別,因為它在模式中的任何位置出現都會產生全域性效果。

沒有修飾詞會讓這成為無操作(所以你為什麼要指定它,除非它是產生的程式碼),而且從 v5.30 開始,會在 use re 'strict' 下發出警告。

(?:pattern)
(?adluimnsx-imnsx:pattern)
(?^aluimnsx:pattern)

這是用於群組,而不是擷取;它會對子表達式進行群組,例如 "()",但不會像 "()" 那樣建立反向參照。因此

@fields = split(/\b(?:a|b|c)\b/)

@fields = split(/\b(a|b|c)\b/)

相符的欄位分隔符號相同,但不會將分隔符號本身作為額外欄位吐出(即使這是 perlfunc 中的 "split" 在其模式包含擷取群組時的行為)。如果你不需要擷取字元,不擷取字元也會更省。

"?"":" 之間的任何字母都作為旗標修飾詞,就像 (?adluimnsx-imnsx) 一樣。例如,

/(?s-i:more.*than).*million/i

等同於更冗長的

/(?:(?s-i)more.*than).*million/i

請注意,除非 /n 修飾詞有效,否則包含在此結構中的任何 () 結構仍會擷取。

就像 "(?adlupimnsx-imnsx)" 結構一樣,aa"a" 會互相覆寫,xx"x" 也是如此。它們不是相加的。因此,執行類似 (?xx-x:foo) 的動作會關閉與相符 foo 相關的所有 "x" 行為。

從 Perl 5.14 開始,"^"(插入符號或揚抑符)緊接在 "?" 之後,為 d-imnsx 的簡寫等效符號。任何正向旗標(除了 "d")都可以出現在插入符號之後,因此

(?^x:foo)

等同於

(?x-imns:foo)

插入符號告訴 Perl 這個群組不會繼承任何周圍模式的旗標,而是使用系統預設值(d-imnsx),並修改為任何指定的旗標。

插入符號允許更簡單地將已編譯的正規表示式字串化。它們看起來像

(?^:pattern)

任何非預設旗標出現在插入符號和冒號之間。因此,檢視此類字串化的測試不需要在其中硬編碼系統預設旗標,只需插入符號即可。如果 Perl 新增了新旗標,插入符號擴充的意義將變更為包含這些旗標的預設值,因此測試仍會運作,且不變更。

在插入符號後指定負旗標為錯誤,因為旗標是多餘的。

(?^...) 的助記符:一個全新的開始,因為插入符號的通常用途是在開頭處比對。

(?|pattern)

這是「分支重設」模式,具有特殊屬性,即擷取群組從每個交替分支中的相同起點開始編號。從 Perl 5.10.0 開始提供。

擷取群組從左到右編號,但在這個結構中,編號會為每個分支重新開始。

每個分支中的編號將會是正常的,而且這個結構之後的任何群組將編號為好像結構只包含一個分支,即其中包含最多擷取群組的分支。

當您想要擷取多個替代比對其中之一時,這個結構很有用。

考量下列模式。底下的數字顯示擷取的內容將儲存在哪個群組中。

# before  ---------------branch-reset----------- after
/ ( a )  (?| x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x
# 1            2         2  3        2     3     4

在將分支重設模式與命名擷取結合使用時要小心。命名擷取實作為持有擷取的編號群組的別名,這會干擾分支重設模式的實作。如果您在分支重設模式中使用命名擷取,最好在每個交替中使用相同的名稱,並以相同的順序

/(?|  (?<a> x ) (?<b> y )
   |  (?<a> z ) (?<b> w )) /x

不這樣做可能會導致意外

"12" =~ /(?| (?<a> \d+ ) | (?<b> \D+))/x;
say $+{a};    # Prints '12'
say $+{b};    # *Also* prints '12'.

這裡的問題在於命名為 a 的群組和命名為 b 的群組都是屬於 $1 的群組的別名。

環顧斷言

環顧斷言是零寬度模式,會比對特定模式,但不會將其包含在 $& 中。正斷言會在子模式比對時比對,負斷言會在子模式失敗時比對。後向環顧會比對到目前比對位置的文字,前向環顧會比對目前比對位置之後的文字。

(?=pattern)
(*pla:pattern)
(*positive_lookahead:pattern)

零寬度正向先行斷言。例如,/\w+(?=\t)/ 匹配一個字詞,後接一個 tab,但不包含 tab 在 $& 中。

(?!pattern)
(*nla:pattern)
(*negative_lookahead:pattern)

零寬度負向先行斷言。例如,/foo(?!bar)/ 匹配任何「foo」的出現,其後不接「bar」。但請注意,先行斷言和後行斷言並非同一件事。您無法將此用於後行斷言。

如果您正在尋找一個「bar」,其前面沒有「foo」,/(?!foo)bar/ 無法滿足您的需求。這是因為 (?!foo) 僅表示下一個東西不能是「foo」——而它不是,它是一個「bar」,因此「foobar」將會匹配。請改用後行斷言(見下文)。

(?<=pattern)
\K
(*plb:pattern)
(*positive_lookbehind:pattern)

零寬度正向後行斷言。例如,/(?<=\t)\w+/ 匹配一個字詞,其前面接一個 tab,但不包含 tab 在 $& 中。

在 Perl 5.30 之前,它僅適用於固定寬度的後行斷言,但從該版本開始,它可以處理 1 到 255 個字元的變動長度,作為一個實驗性功能。如果您使用變動長度的正向後行斷言,該功能將自動啟用。

在 Perl 5.35.10 中,此建構的實驗性質範圍已縮小,並且僅當建構包含擷取括號時才會產生實驗性警告。這些警告會在模式編譯時提出,除非在 experimental::vlb 類別中關閉。這是為了警告您,變動長度正向後行斷言中擷取緩衝區的確切內容並未明確定義,並且可能會在未來的 perl 版本中變更。

目前,如果您在正向變動長度後行斷言中使用擷取緩衝區,結果將是最長且最左邊的可能匹配。這表示

"aax" =~ /(?=x)(?<=(a|aa))/
"aax" =~ /(?=x)(?<=(aa|a))/
"aax" =~ /(?=x)(?<=(a{1,2}?)/
"aax" =~ /(?=x)(?<=(a{1,2})/

都將導致 $1 包含 "aa"。Perl 的未來版本可能會變更此行為。

此結構有一種特殊形式,稱為 \K(自 Perl 5.10.0 起提供),它會導致正規表示式引擎「保留」在 \K 之前比對到的所有內容,並不會將其包含在 $& 中。這實際上提供了非實驗性的任意長度變數長度後向參照。

還有一種技術可用於處理早期版本和長度超過 255 個字元的變數長度後向參照。它在 http://www.drregex.com/2019/02/variable-length-lookbehinds-actually.html 中有說明。

請注意,在 /i 下,幾個單一字元會比對到兩個或三個其他字元。這會讓它們變成變數長度,而 255 長度適用於比對中字元的最大數量。例如,qr/\N{LATIN SMALL LETTER SHARP S}/i 會比對到序列 "ss"。在 /i 下,您的後向參照斷言可以包含 127 個小寫尖銳 S 字元,但加入第 128 個字元會產生編譯錯誤,因為它可能會比對到連續 256 個 "s" 字元。

允許在另一個環顧斷言中使用 \K,但目前尚未明確定義其行為。

基於各種原因,\K 可能比等效的 (?<=...) 結構有效率得多,特別是在您想要有效率地移除字串中某個項目之後的內容時很有用。例如

s/(foo)bar/$1/g;

可以改寫成更有效率的

s/foo\Kbar//g;

如果在結構中的擷取群組中使用非貪婪修飾詞 "?",可能無法獲得預期的結果。

(?<!pattern)
(*nlb:pattern)
(*negative_lookbehind:pattern)

零寬度負向後向參照斷言。例如,/(?<!bar)foo/ 會比對到任何不接在「bar」之後的「foo」出現。

在 Perl 5.30 之前,它僅適用於固定寬度後向參照,但從該版本開始,它可以作為實驗性功能處理 1 到 255 個字元的變數長度。如果您使用變數長度負向後向參照斷言,此功能會自動啟用。

在 Perl 5.35.10 中,此結構的實驗性質範圍已縮小,只有當結構包含擷取括號時才會產生實驗性警告。除非關閉,否則這些警告會在模式編譯期間在 experimental::vlb 類別中提出。這是為了警告您,變數長度負向後向參照中擷取緩衝區的確切內容尚未明確定義,並且可能會在未來的 Perl 版本中變更。

目前,如果您在負向變數長度後向參照中使用擷取緩衝區,結果可能不是您預期的,例如

say "axfoo"=~/(?=foo)(?<!(a|ax)(?{ say $1 }))/ ? "y" : "n";

將輸出下列內容

a
no

這沒有道理,因為這應印出「ax」,因為「a」沒有對齊在正確的位置。另一個範例是

say "yes: '$1-$2'" if "aayfoo"=~/(?=foo)(?<!(a|aa)(a|aa)x)/;

將輸出下列內容

yes: 'aa-a'

在未來的 perl 發行版本中,我們可能會變更此行為,讓這兩個範例產生較合理的輸出。

請注意,我們確信此建構會比對並適當地拒絕樣式,未定義的行為僅與比對期間或比對後擷取緩衝區的值有關。

有一種技術可用於處理早期版本中變動長度的後向觀察,以及長度超過 255 個字元的後向觀察。其說明記載於 http://www.drregex.com/2019/02/variable-length-lookbehinds-actually.html

請注意,在 /i 下,幾個單一字元會比對到兩個或三個其他字元。這會讓它們變成變數長度,而 255 長度適用於比對中字元的最大數量。例如,qr/\N{LATIN SMALL LETTER SHARP S}/i 會比對到序列 "ss"。在 /i 下,您的後向參照斷言可以包含 127 個小寫尖銳 S 字元,但加入第 128 個字元會產生編譯錯誤,因為它可能會比對到連續 256 個 "s" 字元。

如果在結構中的擷取群組中使用非貪婪修飾詞 "?",可能無法獲得預期的結果。

(?<NAME>pattern)
(?'NAME'pattern)

命名擷取群組。在各方面都與一般擷取括號 () 相同,但額外的好處是可以在各種正規表示式建構中透過名稱來參照群組 (例如 \g{NAME}),並可以在比對成功後透過 %+%- 根據名稱來存取群組。請參閱 perlvar,以進一步了解 %+%- 雜湊。

如果多個不同的擷取群組具有相同名稱,則 $+{NAME} 將參照比對中最左邊已定義的群組。

形式 (?'NAME'pattern)(?<NAME>pattern) 是等效的。

注意:雖然此建構的表示法與 .NET 正規表示式中類似的函數相同,但行為不同。在 Perl 中,群組會依序編號,無論是否命名。因此,在樣式中

/(x)(?<foo>y)(z)/

$+{foo} 會與 $2 相同,而 $3 會包含 'z',而不是相反的情況,這正是 .NET 正規表示式駭客所預期的。

目前 NAME 僅限於簡單識別碼。換句話說,它必須符合 /^[_A-Za-z][_A-Za-z0-9]*\z/ 或其 Unicode 延伸 (請參閱 utf8),儘管它不會由區域設定延伸 (請參閱 perllocale)。

注意:為了讓有 Python 或 PCRE regex 引擎經驗的程式設計師更輕鬆,可以使用模式 (?P<NAME>pattern) 取代 (?<NAME>pattern);不過,此表單不支援使用單引號作為名稱的分隔符號。

\k<NAME>
\k'NAME'
\k{NAME}

命名反向參照。類似數字反向參照,但群組是以名稱而非數字指定。如果多個群組具有相同名稱,則它指的是目前比對中最左邊定義的群組。

如果模式中前面沒有由 (?<NAME>) 定義的名稱,則會發生錯誤。

所有三種表單都是等效的,不過使用 \k{ NAME } 時,您可以在大括號內但緊鄰大括號的地方選擇性地加上空白,如所示。

注意:為了讓有 Python 或 PCRE regex 引擎經驗的程式設計師更輕鬆,可以使用模式 (?P=NAME) 取代 \k<NAME>

(?{ code })

警告:要安全地使用此功能,您必須了解其限制。執行具有副作用的程式碼可能無法在不同版本間執行相同,因為 regex 引擎在未來最佳化時會受到影響。如需更多資訊,請參閱"嵌入式程式碼執行頻率"

此零寬度斷言會執行任何嵌入式 Perl 程式碼。它總是會成功,而且其回傳值會設定為 $^R

在文字模式中,程式碼會與周圍程式碼在同一時間進行剖析。在模式中時,控制權會暫時傳回 perl 剖析器,直到遇到邏輯上平衡的閉合大括號為止。這類似於處理文字字串中陣列索引運算式的方式,例如

"abc$array[ 1 + f('[') + g()]def"

特別是,大括號不需要平衡

s/abc(?{ f('{'); })/def/

即使在執行時內插和編譯的模式中,文字程式碼區塊也會在 perl 編譯時編譯一次;下列會印出 "ABCD"

print "D";
my $qr = qr/(?{ BEGIN { print "A" } })/;
my $foo = "foo";
/$foo$qr(?{ BEGIN { print "B" } })/;
BEGIN { print "C" }

在程式碼文字不是直接出現在原始碼 /模式/ 中,而是從執行時資訊衍生的模式中,程式碼會在編譯模式的同時編譯,而且基於安全性考量,use re 'eval' 必須在範圍內。這是為了防止包含程式碼片段的使用者提供模式被執行。

在您需要使用 use re 'eval' 啟用此功能的情況下,如果您的 perl 支援,您也應該啟用 taint 檢查。更好的方法是,在 Safe 區隔中使用經過仔細約束的評估。請參閱 perlsec,以取得關於這兩種機制的詳細資料。

從解析、詞彙變數範圍和封閉的觀點來看,

/AAA(?{ BBB })CCC/

行為近似於

/AAA/ && do { BBB } && /CCC/

類似地,

qr/AAA(?{ BBB })CCC/

行為近似於

sub { /AAA/ && do { BBB } && /CCC/ }

特別是

{ my $i = 1; $r = qr/(?{ print $i })/ }
my $i = 2;
/$r/; # prints "1"

(?{...}) 區塊內,$_ 參照正規表示式比對的字串。您也可以使用 pos() 來得知目前在這個字串中比對的位置。

從詞彙變數宣告的角度來看,程式區塊會引入新的範圍,但不會local 和類似的區域化行為的角度來看。因此,同一個模式中後面的程式區塊仍然會看到在前面區塊中區域化的值。這些累積的區域化會在成功比對的結尾處或斷言回溯時取消(比較 "回溯")。例如,

$_ = 'a' x 8;
m<
   (?{ $cnt = 0 })               # Initialize $cnt.
   (
     a
     (?{
         local $cnt = $cnt + 1;  # Update $cnt,
                                 # backtracking-safe.
     })
   )*
   aaaa
   (?{ $res = $cnt })            # On success copy to
                                 # non-localized location.
 >x;

最初會將 $cnt 增加到 8;然後在回溯期間,它的值會回溯到 4,也就是指定給 $res 的值。在正規表示式執行結束時,$cnt 會回溯到它的初始值 0。

此斷言可用作

(?(condition)yes-pattern|no-pattern)

switch 中的條件。如果沒有以這種方式使用,code 的評估結果會放入特殊變數 $^R 中。這會立即發生,因此可以在同一個正規表示式內的其他 (?{ code }) 斷言中使用 $^R

上面對 $^R 的指定已適當地區域化,因此如果斷言回溯,會還原 $^R 的舊值;比較 "回溯"

請注意,特殊變數 $^N 與程式區塊搭配使用特別有用,可以在變數中擷取子比對的結果,而不用追蹤巢狀括號的數量。例如

$_ = "The brown fox jumps over the lazy dog";
/the (\S+)(?{ $color = $^N }) (\S+)(?{ $animal = $^N })/i;
print "color = $color, animal = $animal\n";

使用此建構會在模式中全面停用一些最佳化,因此模式可能會因此執行得慢很多。使用 * 取代 ? 區塊,以建立此建構的樂觀形式。(*{ ... }) 不應該停用任何最佳化。

(*{ code })

這與 (?{ code }) 完全相同,但例外的是,它完全不會停用正規表示式引擎中的任何最佳化。它執行得有多頻繁可能會因 perl 版本而異。在失敗的比對中,它甚至可能完全不會執行。

(??{ code })

警告:要安全地使用此功能,您必須了解其限制。執行具有副作用的程式碼可能無法在不同版本間執行相同,因為 regex 引擎在未來最佳化時會受到影響。如需更多資訊,請參閱"嵌入式程式碼執行頻率"

這是一個「延後」的正規子表示式。它的行為與上面描述的 (?{ code }) 程式區塊完全相同,但它的回傳值不會指定給 $^R,而是視為一個模式,如果它是一個字串,就會編譯(或如果它是一個 qr// 物件,就會照樣使用),然後比對,就像它插入此建構一樣。

在比對此子模式期間,它有自己的一組擷取,在子比對期間有效,但一旦控制權傳回主模式,就會捨棄。例如,以下會比對,內部模式擷取「B」並比對「BB」,而外部模式擷取「A」;

my $inner = '(.)\1';
"ABBA" =~ /^(.)(??{ $inner })\1/;
print $1; # prints "A";

請注意,這表示內部模式無法參照外部定義的擷取群組。(程式碼區塊本身可以使用 $1 等來參照封裝模式的擷取群組。)因此,儘管

('a' x 100)=~/(??{'(.)' x 100})/

相符,但不會在結束時設定 $1

下列模式相符括號群組

$re = qr{
           \(
           (?:
              (?> [^()]+ )  # Non-parens without backtracking
            |
              (??{ $re })   # Group with matching parens
           )*
           \)
        }x;

另請參閱 (?PARNO),這是完成相同任務的另一種更有效率的方式。

在未消耗任何輸入字串的情況下執行延後正規表示式太多次也會導致致命錯誤。發生這種情況的深度編譯到 Perl 中,因此可以使用自訂建置變更。

使用此建構會在模式中全面停用某些最佳化,因此模式執行速度可能會因此慢很多。

(?PARNO) (?-PARNO) (?+PARNO) (?R) (?0)

遞迴子模式。將目前模式中特定擷取緩衝區的內容視為獨立子模式,並嘗試在字串中的目前位置相符。呼叫者關於後向參照等事項的擷取狀態資訊可供子模式使用,但子模式設定的擷取緩衝區對呼叫者不可見。

類似於 (??{ code }),但它不涉及執行任何程式碼或潛在編譯傳回的模式字串;而是將特定擷取群組中包含的目前模式部分視為必須在目前位置相符的獨立模式。另一個不同之處是擷取緩衝區的處理,與 (??{ code }) 不同,遞迴模式可以存取其呼叫者的相符狀態,因此可以安全地使用後向參照。

PARNO 是一連串數字(不可開頭為 0),其值反映要遞迴的擷取群組的括號編號。(?R) 遞迴至整個樣式的開頭。(?0)(?R) 的替代語法。如果 PARNO 前面有正號或負號,則假設它是相對的,負數表示前一個擷取群組,正數表示後一個。因此 (?-1) 參照最近宣告的群組,而 (?+1) 表示要宣告的下一組。請注意,相對遞迴的計數與相對回溯參考不同,因為在遞迴中未封閉的群組包含在內。

以下樣式會比對一個函式 foo(),其參數可能包含平衡括號。

$re = qr{ (                   # paren group 1 (full function)
            foo
            (                 # paren group 2 (parens)
              \(
                (             # paren group 3 (contents of parens)
                (?:
                 (?> [^()]+ ) # Non-parens without backtracking
                |
                 (?2)         # Recurse to start of paren group 2
                )*
                )
              \)
            )
          )
        }x;

如果樣式如下使用

'foo(bar(baz)+baz(bop))'=~/$re/
    and print "\$1 = $1\n",
              "\$2 = $2\n",
              "\$3 = $3\n";

產生的輸出應該是以下

$1 = foo(bar(baz)+baz(bop))
$2 = (bar(baz)+baz(bop))
$3 = bar(baz)+baz(bop)

如果沒有定義對應的擷取群組,則會發生致命錯誤。在未消耗任何輸入字串的情況下深度遞迴也會導致致命錯誤。發生這種情況的深度會編譯到 Perl 中,因此可以使用自訂建置來變更。

以下顯示如何使用負數索引,讓遞迴樣式更容易嵌入到 qr// 建構中以供後續使用

my $parens = qr/(\((?:[^()]++|(?-1))*+\))/;
if (/foo $parens \s+ \+ \s+ bar $parens/x) {
   # do something here...
}

請注意,此樣式與相同形式的等效 PCRE 或 Python 建構的行為不同。在 Perl 中,您可以回溯到遞迴群組,而在 PCRE 和 Python 中,遞迴到的群組會被視為原子。此外,修改項會在編譯時解析,因此像 (?i:(?1))(?:(?i)(?1)) 這樣的建構不會影響子樣式的處理方式。

(?&NAME)

遞迴到一個命名子樣式。與 (?PARNO) 相同,但遞迴到的括號是由名稱決定的。如果多個括號具有相同名稱,則遞迴到最左邊的括號。

參照樣式中某處未宣告的名稱會發生錯誤。

注意:為了讓有 Python 或 PCRE 正規表示式引擎經驗的程式設計師更容易上手,可以使用 (?P>NAME) 樣式,而不是 (?&NAME)

(?(condition)yes-pattern|no-pattern)
(?(condition)yes-pattern)

條件式。如果 condition 產生真值,則比對 yes-pattern,否則比對 no-pattern。遺失的樣式永遠會比對成功。

(條件) 應為下列其中之一

括號中的整數

(如果相應的括號配對有效)

前瞻/後瞻/評估零寬度斷言
尖括號或單引號中的名稱

(如果具有給定名稱的群組配對有效)

特殊符號 (R)

(在遞迴或 eval 內部評估時為真)。此外,"R" 後面可以接一個數字 (在適當的群組內部遞迴時評估時為真),或接 &NAME,這種情況下只有在命名群組內遞迴時評估時才為真。

以下是可能的謂詞摘要

(1) (2) ...

檢查編號的擷取群組是否配對到某個項目。完整語法:(?(1)then|else)

(<NAME>) ('NAME')

檢查具有給定名稱的群組是否配對到某個項目。完整語法:(?(<name>)then|else)

(?=...) (?!...) (?<=...) (?<!...)

檢查樣式是否配對 (或不配對,針對 "!" 變體)。完整語法:(?(?=lookahead)then|else)

(?{ CODE })

將程式碼區塊的回傳值視為條件。完整語法:(?(?{ CODE })then|else)

請注意,使用此結構可能會影響樣式的整體效能。請考慮使用 (*{ CODE })

(*{ CODE })

將程式碼區塊的回傳值視為條件。完整語法:(?(*{ CODE })then|else)

(R)

檢查表達式是否在遞迴內部評估。完整語法:(?(R)then|else)

(R1) (R2) ...

檢查表達式是否在第 n 個擷取群組內部直接執行時評估。此檢查等同於正規表示法的

if ((caller(0))[3] eq 'subname') { ... }

換句話說,它不會檢查完整的遞迴堆疊。

完整語法:(?(R1)then|else)

(R&NAME)

(R1) 類似,此謂詞檢查我們是否直接在具有給定名稱的最左邊組中執行(這是 (?&NAME) 用於消除歧義的相同邏輯)。它不檢查完整堆疊,只檢查最內層活動遞迴的名稱。完整語法:(?(R&name)then|else)

(DEFINE)

在這種情況下,yes-pattern 永遠不會直接執行,也不允許 no-pattern。與 (?{0}) 精神相似,但更有效率。詳情請見下方。完整語法:(?(DEFINE)definitions...)

例如

m{ ( \( )?
   [^()]+
   (?(1) \) )
 }x

符合一塊非括號,可能包含在括號中。

一種特殊形式是 (DEFINE) 謂詞,它永遠不會直接執行其 yes-pattern,也不允許 no-pattern。這允許定義只會由遞迴機制執行的子模式。這樣,您可以定義一組正規表示式規則,可以將它們捆綁到您選擇的任何模式中。

建議您將 DEFINE 區塊放在模式的結尾,並命名其中定義的任何子模式。

此外,值得注意的是,以這種方式定義的模式可能不會那麼有效率,因為最佳化器在處理它們時並不靈光。

以下是如何使用它的範例

/(?<NAME>(?&NAME_PAT))(?<ADDR>(?&ADDRESS_PAT))
 (?(DEFINE)
   (?<NAME_PAT>....)
   (?<ADDRESS_PAT>....)
 )/x

請注意,在遞迴中匹配的擷取群組在遞迴返回後無法存取,因此需要額外的擷取群組層。因此,即使 $+{NAME} 會被定義,$+{NAME_PAT} 也不會被定義。

最後,請記住,在 DEFINE 區塊內建立的子模式會計入擷取的絕對和相對數量,因此

my @captures = "a" =~ /(.)                  # First capture
                       (?(DEFINE)
                           (?<EXAMPLE> 1 )  # Second capture
                       )/x;
say scalar @captures;

將輸出 2,而不是 1。如果您打算使用 qr// 運算子編譯定義,然後將它們內插到另一個模式中,這一點尤其重要。

(?>pattern)
(*atomic:pattern)

一個「獨立」的子表達式,它匹配一個獨立的 pattern 如果錨定在給定位置會匹配的子字串,而且它只匹配 這個子字串。這個結構對於最佳化原本會是「永遠」匹配的內容很有用,因為它不會回溯(請參閱 「回溯」)。它也可能在「盡可能擷取,不放棄任何東西」的語義有用的地方使用。

例如:^(?>a*)ab 永遠不會匹配,因為 (?>a*)(如上所述,錨定在字串開頭)將匹配字串開頭的所有字元 "a",沒有留下 "a"ab 匹配。相比之下,a*ab 將匹配與 a+b 相同的內容,因為子群組 a* 的匹配受後續群組 ab 的影響(請參閱 "回溯")。特別是,a*ab 內部的 a* 將匹配比獨立的 a* 更少的字元,因為這會使尾部匹配。

(?>pattern) 在匹配後不會完全停用回溯。仍然可以回溯到該結構的前面,但不能回溯到結構內部。因此,((?>a*)|(?>b*))ar 仍然會匹配 "bar"。

可以透過撰寫 (?=(pattern))\g{-1} 來達成類似於 (?>pattern) 的效果。這會匹配與獨立的 a+ 相同的子字串,而後續的 \g{-1} 會吃掉匹配的字串;因此,它會將零長度斷言變成類比於 (?>...) 的斷言。(這兩個結構之間的差異在於,第二個結構使用捕獲群組,因此會轉移正規表示式中其餘部分的反向引用的序數。)

考慮這個模式

m{ \(
      (
        [^()]+           # x+
      |
        \( [^()]* \)
      )+
   \)
 }x

這將有效率地匹配一個非空群組,其中匹配的括號深度為兩層或更淺。但是,如果沒有這樣的群組,它將在長字串上花費幾乎永遠的時間。這是因為有許多不同的方式可以將長字串分割成幾個子字串。這就是 (.+)+ 所做的,而 (.+)+ 類似於上述模式的子模式。考慮上述模式如何在幾秒鐘內偵測到 ((()aaaaaaaaaaaaaaaaaa 上的非匹配,但每個額外的字母都會將此時間加倍。這種指數效能會讓您的程式看起來像當機了。但是,對這個模式做一個微小的變更

m{ \(
      (
        (?> [^()]+ )        # change x+ above to (?> x+ )
      |
        \( [^()]* \)
      )+
   \)
 }x

它使用 (?>...) 在上述模式匹配時完全匹配(自行驗證這一點將是一個有益的練習),但在用於包含 1000000 個 "a" 的類似字串時,完成時間為上述模式的四分之一。但是,請注意,當這個結構後接量詞時,它目前會觸發 use warnings pragma 或 -w 開關下的警告訊息,指出它"在正規表示式中多次匹配空字串"

在簡單的群組上,例如模式 (?> [^()]+ ),可以使用負向先行斷言來達成類似效果,例如 [^()]+ (?! [^()] )。這在包含 1000000 個 "a" 的字串上僅慢了 4 倍。

在許多情況下,"盡可能搶奪,絕不歸還" 的語意是理想的,而乍看之下,一個簡單的 ()* 似乎是正確的解決方案。假設我們解析的文字中,註解以 "#" 後接一些選用的(水平)空白字元為分隔符號。與其外觀相反,#[ \t]* 不是 匹配註解分隔符號的正確子表示式,因為如果可以透過這種方式使模式的其餘部分匹配,它可能會 "放棄" 一些空白字元。正確的答案是下列其中一個

(?>#[ \t]*)
#[ \t]*(?![ \t])

例如,若要將非空註解擷取到 $1,應該使用下列其中一個

/ (?> \# [ \t]* ) (        .+ ) /x;
/     \# [ \t]*   ( [^ \t] .* ) /x;

您選擇哪一個取決於哪一個表達式較能反映上述的註解規格。

在某些文獻中,此結構稱為「原子比對」或「所有格比對」。

所有格量詞等於將它們套用的項目置於這些結構之一內部。套用下列等價關係

Quantifier Form     Bracketing Form
---------------     ---------------
PAT*+               (?>PAT*)
PAT++               (?>PAT+)
PAT?+               (?>PAT?)
PAT{min,max}+       (?>PAT{min,max})

巢狀的 (?>...) 結構並非無效操作,即使乍看之下它們似乎是。這是因為巢狀的 (?>...) 可以限制否則可能會發生的內部回溯。例如,

"abc" =~ /(?>a[bc]*c)/

符合,但

"abc" =~ /(?>a(?>[bc]*)c)/

不符合。

(?[ ])

請參閱 "perlrecharclass 中的「擴充方括號字元類別」

回溯

注意:本節提供正規表示式行為的抽象近似值。如需更嚴謹(且複雜)的觀點,以瞭解在可能的選項中選擇比對時所涉及的規則,請參閱 "組合 RE 片段"

正規表示式比對的一項基本功能涉及稱為回溯的概念,目前所有正規非所有格表示式量詞(即 "*"*?"+"+?{n,m}{n,m}?)在需要時都會使用此概念。回溯通常會在內部最佳化,但此處概述的一般原則仍然有效。

正規表示式要符合,整個正規表示式都必須符合,而不能只符合一部分。因此,如果包含量詞的圖樣的開頭以導致圖樣中後續部分失敗的方式成功,比對引擎就會備份並重新計算開頭部分,這就是稱為回溯的原因。

以下是回溯的範例:假設您要在字串「Food is on the foo table」中尋找「foo」後面的字詞。

$_ = "Food is on the foo table.";
if ( /\b(foo)\s+(\w+)/i ) {
    print "$2 follows $1.\n";
}

當比對執行時,正規表示式的第一部分 (\b(foo)) 在字串開頭處找到可能的比對,並將「Foo」載入 $1。然而,比對引擎一看到 $1 中儲存的「Foo」後面沒有空白,便會意識到自己的錯誤,並從暫定比對處往後一個字元重新開始。這一次,它一直執行到下一個「foo」出現。完整的正規表示式這次符合,您會得到預期的輸出「table follows foo」。

有時,最小比對可以提供很大的幫助。想像一下您想比對「foo」和「bar」之間的所有內容。一開始,您會撰寫類似這樣的內容

$_ =  "The food is under the bar in the barn.";
if ( /foo(.*)bar/ ) {
    print "got <$1>\n";
}

這可能會意外產生

got <d is under the bar in the >

這是因為 .* 是貪婪的,因此您會得到第一個「foo」和最後一個「bar」之間的所有內容。在此,使用最小比對會更有效,以確保您得到「foo」和之後第一個「bar」之間的文字。

  if ( /foo(.*?)bar/ ) { print "got <$1>\n" }
got <d is under the >

以下是另一個範例。假設您想比對字串結尾的數字,而且也想保留比對的前一部分。因此您寫了這段

$_ = "I have 2 numbers: 53147";
if ( /(.*)(\d*)/ ) {                                # Wrong!
    print "Beginning is <$1>, number is <$2>.\n";
}

這完全不會運作,因為 .* 貪婪地吞掉了整個字串。由於 \d* 可以比對空字串,因此完整的正規表示式比對成功。

Beginning is <I have 2 numbers: 53147>, number is <>.

以下是某些變體,其中大部分無法運作

$_ = "I have 2 numbers: 53147";
@pats = qw{
    (.*)(\d*)
    (.*)(\d+)
    (.*?)(\d*)
    (.*?)(\d+)
    (.*)(\d+)$
    (.*?)(\d+)$
    (.*)\b(\d+)$
    (.*\D)(\d+)$
};

for $pat (@pats) {
    printf "%-12s ", $pat;
    if ( /$pat/ ) {
        print "<$1> <$2>\n";
    } else {
        print "FAIL\n";
    }
}

這將會列印

(.*)(\d*)    <I have 2 numbers: 53147> <>
(.*)(\d+)    <I have 2 numbers: 5314> <7>
(.*?)(\d*)   <> <>
(.*?)(\d+)   <I have > <2>
(.*)(\d+)$   <I have 2 numbers: 5314> <7>
(.*?)(\d+)$  <I have 2 numbers: > <53147>
(.*)\b(\d+)$ <I have 2 numbers: > <53147>
(.*\D)(\d+)$ <I have 2 numbers: > <53147>

您會看到,這可能會有點棘手。重要的是要了解正規表示式只不過是一組斷言,用來定義成功。針對特定字串,定義可能會有 0、1 或好幾種不同的成功方式。而且如果有多種成功方式,您需要了解回溯,才能知道您將達成哪種類型的成功。

當使用前瞻斷言和否定時,這一切可能會變得更加棘手。想像一下您想找出一個非數字的序列,其後不接「123」。您可能會試著寫成

$_ = "ABC123";
if ( /^\D*(?!123)/ ) {                # Wrong!
    print "Yup, no 123 in $_\n";
}

但這不會比對成功;至少不是您所希望的方式。它宣稱字串中沒有 123。以下是這個模式比對成功的原因,與一般預期相反

$x = 'ABC123';
$y = 'ABC445';

print "1: got $1\n" if $x =~ /^(ABC)(?!123)/;
print "2: got $1\n" if $y =~ /^(ABC)(?!123)/;

print "3: got $1\n" if $x =~ /^(\D*)(?!123)/;
print "4: got $1\n" if $y =~ /^(\D*)(?!123)/;

這會列印

2: got ABC
3: got AB
4: got ABC

您可能預期測試 3 會失敗,因為它看起來像是測試 1 的更通用的版本。它們之間的重要差異在於測試 3 包含一個量詞 (\D*),因此可以使用回溯,而測試 1 則不行。發生的事情是,您詢問「在 $x 的開頭,在 0 個或更多非數字之後,您是否有某個不是 123 的東西?」如果模式比對器讓 \D* 擴充為「ABC」,這將會導致整個模式失敗。

搜尋引擎最初會將 \D* 與「ABC」比對。然後它會嘗試將 (?!123) 與「123」比對,這會失敗。但由於正規表示式中使用了量詞 (\D*),因此搜尋引擎可以回溯並重新嘗試比對,希望比對完整的正規表示式。

這個模式真的、真的 很想成功,因此它使用標準模式後退重試,並讓 \D* 這次只擴充為「AB」。現在「AB」之後確實有某個不是「123」的東西。它是「C123」,這就夠了。

我們可以使用斷言和否定來處理這個問題。我們會說 $1 中的第一部分必須接在一個數字和一個不是「123」的東西之後。請記住,前瞻是零寬度表示式,它們只會查看,但不會在比對中使用任何字串。因此,用這種方式改寫會產生您預期的結果;也就是說,案例 5 會失敗,但案例 6 會成功

print "5: got $1\n" if $x =~ /^(\D*)(?=\d)(?!123)/;
print "6: got $1\n" if $y =~ /^(\D*)(?=\d)(?!123)/;

6: got ABC

換句話說,兩個零寬度斷言並排在一起的作用就像它們一起使用 AND,就像您使用任何內建斷言一樣:/^$/ 僅當您同時位於行首 AND 行尾時才會匹配。更深層的基本原理是,正規表達式中的並置始終表示 AND,除非您使用垂直線寫入明確的 OR。/ab/ 表示匹配「a」AND(然後)匹配「b」,儘管嘗試匹配是在不同位置進行的,因為「a」不是零寬度斷言,而是一寬度斷言。

警告:特別複雜的正規表達式可能會花費指數時間才能解決,因為它們可以使用回溯嘗試匹配的可能方式極多。例如,如果不使用正規表達式引擎執行的內部最佳化,則執行這項操作將需要非常長的時間

'aaaaaaaaaaaa' =~ /((a{0,5}){0,5})*[c]/

如果您在內部群組中使用 "*",而不是將它們限制為 0 到 5 次匹配,那麼它將永遠執行下去,或者直到您用盡堆疊空間。此外,這些內部最佳化並不總是適用的。例如,如果您在外部群組中放置 {0,5} 而不是 "*",則沒有適用的當前最佳化,並且匹配需要很長時間才能完成。

最佳化此類巨獸的強大工具是所謂的「獨立群組」,它不會回溯(請參閱 "(?>pattern)")。另請注意,零長度向前/向後斷言不會回溯以進行尾部匹配,因為它們位於「邏輯」上下文中:僅考慮它們是否匹配是否相關。有關預先檢視的副作用可能影響後續匹配的範例,請參閱 "(?>pattern)"

腳本執行

腳本執行基本上是一系列字元,全部來自同一個 Unicode 腳本(請參閱 perlunicode 中的「腳本」),例如拉丁文或希臘文。在大多數地方,一個單字永遠不會以多個腳本寫成,除非它是欺騙攻擊。一個臭名昭著的範例是

paypal.com

這些字母可以全部都是拉丁字母(如上方的範例),或者全部都是西里爾字母(點除外),或者兩者混合。在網際網路地址的情況下,.com 會是拉丁字母,而任何西里爾字母都會導致它變成混合,而不是腳本執行。點擊此類連結的人不會被導向真正的 Paypal 網站,而是攻擊者會製作一個相似的網站,試圖從該人收集敏感資訊。

從 Perl 5.28 開始,現在可以輕鬆偵測到不是腳本執行的字串。只要將任何模式都包含在以下任一種模式中即可

(*script_run:pattern)
(*sr:pattern)

發生的事情是,在 pattern 成功配對後,它會受到額外條件的約束,即其中的每個字元都必須來自相同的腳本(請參閱以下例外)。如果這不是真的,則會回溯,直到找到全部都在同一個腳本中且相符的內容,或者用盡所有可能性。這可能會導致大量的回溯,但通常只有惡意輸入才會導致這種情況,儘管速度變慢可能會導致拒絕服務攻擊。如果您的需求允許,最好將模式設為原子模式,以減少回溯量。這很可能是您想要的,所以您不必寫這個

(*script_run:(?>pattern))

您可以寫以下任一種

(*atomic_script_run:pattern)
(*asr:pattern)

(請參閱 "(?>pattern)"。)

在台灣、日本和韓國,文字通常包含來自其原生腳本和基本中文的字元混合。Perl 遵循 Unicode 的 UTS 39 (https://unicode.org/reports/tr39/) Unicode 安全機制,允許這種混合。例如,日文腳本片假名和假名通常在實務上混合在一起,並加上一些中文字元,因此 Perl 將它們視為單一腳本執行。

用於配對十進位數字的規則稍微嚴格一些。許多腳本都有自己的一組數字,等同於西方的 09。少數,例如阿拉伯語,有多於一組。對於一個字串要被視為腳本執行,其中的所有數字都必須來自同一個十進位組,由遇到的第一個數字決定。例如,

qr/(*script_run: \d+ \b )/x

保證配對的數字都來自同一個 10 進位組。您不會從具有不同值的不同腳本中得到相似的數字。

Unicode 有三個偽腳本,會特別處理。

「未知」套用於尚未確定其意義的碼點。Perl 目前會將由這些碼點之一組成的任何單一字元字串比對為腳本執行。但任何包含這些碼點之一且長度大於一個碼點的字串,將不被視為腳本執行。

「繼承」套用於修改其他字元的字元,例如某種類型的重音。這些字元被視為屬於主字元的腳本,因此絕不會導致腳本執行不匹配。

另一個是「通用」。這主要包含標點符號、表情符號、用於數學和音樂的字元、ASCII 數字 09,以及這些數字的全形形式。這些字元可以出現在世界上許多腳本的文字中。這些字元也不會導致腳本執行不匹配。但與其他腳本一樣,執行中的所有數字都必須來自相同的 10 個數字組。

此結構不擷取。如果需要,您可以新增括號到 樣式 以進行擷取。如果您計畫使用 "(*ACCEPT) (*ACCEPT:arg)" 並且不希望它繞過腳本執行檢查,則必須這麼做。

由 UTS 39 (https://unicode.org/reports/tr39/) 修改的 Script_Extensions 屬性用作此功能的基礎。

總而言之,

特殊回溯控制動詞

這些特殊樣式通常為 (*VERB:arg) 形式。除非另有說明,否則 arg 參數是選用的;在某些情況下,它是強制性的。

包含允許參數的特殊回溯動詞的任何樣式都具有特殊行為,即在執行時會設定目前套件的 $REGERROR$REGMARK 變數。執行時套用下列規則

如果失敗,$REGERROR 變數將設定為動詞模式的 arg 值,如果動詞參與匹配失敗。如果模式的 arg 部分被省略,則 $REGERROR 將設定為執行最後一個 (*MARK:NAME) 模式的名稱,或如果沒有的話,則設定為 TRUE。此外,$REGMARK 變數將設定為 FALSE。

如果匹配成功,則 $REGERROR 變數將設定為 FALSE,而 $REGMARK 變數將設定為執行最後一個 (*MARK:NAME) 模式的名稱。有關更多詳細資訊,請參閱下方 (*MARK:NAME) 動詞的說明。

注意: $REGERROR$REGMARK 不是像 $1 和大多數其他 regex 相關變數那樣的魔術變數。它們不是特定於範圍,也不是唯讀,而是類似於 $AUTOLOAD 的易變套件變數。它們設定在包含執行 regex 的程式碼的套件中(而不是編譯它的套件,如果它們不同)。如果需要,您可以在執行 regex 之前使用 local 將這些變數的變更定位到特定範圍。

如果模式不包含允許引數的特殊回溯動詞,則 $REGERROR$REGMARK 完全不會被觸發。

動詞
(*PRUNE) (*PRUNE:NAME)

當回溯到失敗時,此零寬度模式會修剪當前點的回溯樹。考慮模式 /A (*PRUNE) B/,其中 AB 是複雜模式。在到達 (*PRUNE) 動詞之前,A 可能需要回溯以進行匹配。到達後,匹配會繼續在 B 中進行,它也可能需要回溯;但是,如果 B 沒有匹配,則不會再進行回溯,模式將在當前起始位置直接失敗。

以下範例計算模式中所有可能的匹配字串(實際上並未匹配任何字串)。

'aaab' =~ /a+b?(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";

產生

aaab
aaa
aa
a
aab
aa
a
ab
a
Count=9

如果我們在計數之前加入一個 (*PRUNE),如下所示

'aaab' =~ /a+b?(*PRUNE)(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";

我們會防止回溯並在每個匹配起始點找到最長匹配字串的計數,如下所示

aaab
aab
ab
Count=3

可以在模式中使用任意數量的 (*PRUNE) 斷言。

另請參閱 "(?>pattern)" 和佔有量詞,以取得控制回溯的其他方法。在某些情況下,可以使用 (*PRUNE) 取代 (?>pattern),而沒有功能差異;但是,(*PRUNE) 可用於處理無法僅使用 (?>pattern) 表示的情況。

(*SKIP) (*SKIP:NAME)

此零寬度模式類似於 (*PRUNE),但不同的是,在失敗時,它也表示與執行 (*SKIP) 模式相匹配的任何文字都無法成為此模式的任何匹配的一部分。這實際上表示,如果失敗,正規表示式引擎會「跳過」前進到此位置,並嘗試再次匹配(假設有足夠的空間進行匹配)。

(*SKIP:NAME) 模式的名稱具有特殊意義。如果在匹配時遇到 (*MARK:NAME),則該位置將用作「跳過點」。如果未遇到該名稱的 (*MARK),則 (*SKIP) 運算子無效。在沒有名稱的情況下使用時,「跳過點」是執行 (*SKIP) 模式時的匹配點。

將以下內容與 (*PRUNE) 中的範例進行比較;請注意,字串長度為兩倍

'aaabaaab' =~ /a+b?(*SKIP)(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";

輸出

aaab
aaab
Count=2

當字串開頭的「aaab」已匹配,且 (*SKIP) 已執行後,下一個起始點將是執行 (*SKIP) 時游標所在的位置。

(*MARK:NAME) (*:NAME)

此零寬度模式可用於標記在成功匹配模式的特定部分時字串中達到的點。此標記可以命名。如果回溯到失敗,稍後的 (*SKIP) 模式將跳過前進到該點。允許任何數量的 (*MARK) 模式,並且可以複製NAME 部分。

除了與 (*SKIP) 模式互動之外,(*MARK:NAME) 還可用於「標籤」模式分支,以便在匹配後,程式可以確定哪些模式分支參與了匹配。

當匹配成功時,$REGMARK 變數將設定為參與匹配的最近執行的 (*MARK:NAME) 的名稱。

這可用於確定匹配了模式的哪個分支,而無需為每個分支使用單獨的擷取群組,這反過來又可以提高效能,因為 perl 無法最佳化 /(?:(x)|(y)|(z))/,而像 /(?:x(*MARK:x)|y(*MARK:y)|z(*MARK:z))/ 這樣的模式則可以。

當匹配失敗,且除非另一個動詞參與了匹配失敗並提供了自己的名稱供使用,否則 $REGERROR 變數將設定為最近執行的 (*MARK:NAME) 的名稱。

有關更多詳細資訊,請參閱 "(*SKIP)"

作為捷徑,(*MARK:NAME) 可以寫成 (*:NAME)

(*THEN) (*THEN:NAME)

這類似於 Raku 中的「cut group」運算子 ::。與 (*PRUNE) 一樣,此動詞總是會配對,且在失敗時回溯時,它會導致正規表示式引擎嘗試最內層包圍群組(擷取或其他)中的下一個交替(如果有的話)。就 (*THEN) 而言,(?(條件)是模式|否模式) 的兩個分支不算作交替。

它的名稱來自於觀察,此運算結合交替運算子 ("|") 可以用於建立本質上是基於模式的 if/then/else 區塊

( COND (*THEN) FOO | COND2 (*THEN) BAR | COND3 (*THEN) BAZ )

請注意,如果此運算子用於交替內部,則它的作用與 (*PRUNE) 運算子完全相同。

/ A (*PRUNE) B /

/ A (*THEN) B /

相同,但

/ ( A (*THEN) B | C ) /

/ ( A (*PRUNE) B | C ) /

不同,因為在配對到 A 但在 B 失敗後,(*THEN) 動詞會回溯並嘗試 C;但 (*PRUNE) 動詞只會失敗。

(*COMMIT) (*COMMIT:arg)

這是 Raku 的「commit pattern」<commit>:::。它是一個零寬度模式,類似於 (*SKIP),但不同之處在於,在失敗時回溯時,它會導致配對完全失敗。不會再嘗試透過推進開始指標來進一步尋找有效配對。例如,

'aaabaaab' =~ /a+b?(*COMMIT)(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";

輸出

aaab
Count=1

換句話說,一旦進入 (*COMMIT),如果模式不配對,正規表示式引擎就不會再嘗試在字串的其餘部分進行任何進一步的配對。

(*FAIL) (*F) (*FAIL:arg)

此模式不配對任何內容,且總是失敗。它可用於強制引擎回溯。它等於 (?!),但更容易閱讀。事實上,(?!) 在內部會最佳化為 (*FAIL)。你可以提供一個參數,以便如果配對因為此 FAIL 指令而失敗,則可以從 $REGERROR 取得參數。

它可能只有在與 (?{})(??{}) 結合使用時才有用。

(*ACCEPT) (*ACCEPT:arg)

此模式不配對任何內容,且會導致在遇到 (*ACCEPT) 模式時結束成功的配對,無論字串中實際上是否還有更多內容可以配對。當在巢狀模式中,例如遞迴,或在透過 (??{}) 動態產生的子模式中時,只有最內層的模式會立即結束。

如果 (*ACCEPT) 在捕獲群組內部,則群組會標記為在遇到 (*ACCEPT) 的點結束。例如

'AB' =~ /(A (A|B(*ACCEPT)|C) D)(E)/x;

將會匹配,且 $1 會是 AB$2 會是 "B"$3 沒有設定。如果匹配內部括號中的另一個分支,例如字串 'ACDE',則 "D""E" 也必須匹配。

你可以提供一個引數,它會在匹配完成後在變數 $REGMARK 中提供。

關於使用 \1 而不是 $1 的警告

有些人太習慣寫像這樣的事物

$pattern =~ s/(\W)/\\\1/g;

這對於替換的 RHS 而言是祖父條款 (適用於 \1 到 \9),以避免讓 sed 愛好者感到震驚,但這是一個不好的習慣。這是因為在 PerlThink 中,s/// 的右手邊是一個雙引號字串。在一般的雙引號字串中,\1 表示控制字元 A。\1 的慣用 Unix 含義是為 s/// 臨時拼湊的。但是,如果你養成那樣的習慣,如果你之後新增 /e 修飾詞,你會遇到問題。

s/(\d+)/ \1 + 1 /eg;            # causes warning under -w

或者如果你嘗試執行

s/(\d+)/\1000/;

你無法透過說 \{1}000 來消除歧義,但你可以透過 ${1}000 來修正它。內插的運算不應與匹配反向參照的運算混淆。它們在 s///左側 肯定表示不同的東西。

重複模式匹配零長度子字串

警告:困難的材料 (和散文) 在前面。本節需要重寫。

正規表示式提供了一個簡潔而強大的程式語言。與大多數其他強大工具一樣,強大功能會帶來造成破壞的能力。

這種能力的常見濫用源自於使用正規表示式建立無限迴圈的能力,就像像這樣無害的東西

'foo' =~ m{ ( o? )* }x;

o? 匹配在 "foo" 的開頭,由於匹配不會移動字串中的位置,因此由於 "*" 量詞,o? 會一再匹配。建立類似迴圈的另一種常見方法是使用迴圈修飾詞 /g

@matches = ( 'foo' =~ m{ o? }xg );

print "match: <$&>\n" while 'foo' =~ m{ o? }xg;

split() 暗示的迴圈。

然而,長期的經驗表明,許多程式設計任務可以透過使用可能會匹配零長度子字串的重複子表示式來顯著簡化。以下是簡單的範例

@chars = split //, $string;           # // is not magic in split
($whitewashed = $string) =~ s/()/ /g; # parens avoid magic s// /

因此,Perl 允許這樣的建構,透過強制中斷無限迴圈。對於貪婪量詞 *+{} 給出的較低層級迴圈,以及 /g 修飾詞或 split() 算子等較高層級迴圈,這些規則不同。

當 Perl 偵測到重複的表達式與零長度子字串符合時,較低層級的迴圈會中斷(也就是說,迴圈會中斷)。因此

m{ (?: NON_ZERO_LENGTH | ZERO_LENGTH )* }x;

等同於

m{ (?: NON_ZERO_LENGTH )* (?: ZERO_LENGTH )? }x;

例如,這個程式

#!perl -l
"aaaaab" =~ /
  (?:
     a                 # non-zero
     |                 # or
    (?{print "hello"}) # print hello whenever this
                       #    branch is tried
    (?=(b))            # zero-width assertion
  )*  # any number of times
 /x;
print $&;
print $1;

會列印

hello
aaaaa
b

請注意,"hello" 只會列印一次,因為當 Perl 看到最外層 (?:)* 的第六次反覆運算與零長度字串符合時,它會停止 "*"

較高層級的迴圈會在反覆運算之間保留額外的狀態:最後一次符合是否為零長度。若要中斷迴圈,零長度符合之後的下一筆符合不得為零長度。此禁止會與回溯互動(請參閱 "回溯"),因此如果最佳符合為零長度,則會選取次佳符合。

例如

$_ = 'bar';
s/\w??/<$&>/g;

會產生 <><b><><a><><r><>。在字串的每個位置,非貪婪 ?? 給出的最佳符合為零長度符合,而次佳符合則是由 \w 符合的。因此,零長度符合會與一個字元長的符合交替出現。

類似地,對於重複的 m/()/g,次佳符合為字串中再往後一個刻度的符合。

與零長度符合 的額外狀態會與符合的字串相關聯,並會由每個指派至 pos() 重設。在 split 期間,會忽略前一次符合末端的零長度符合。

組合正規表示式片段

之前描述的每個正規表示式基本片段(例如 ab\Z)最多只能在輸入字串的特定位置符合一個子字串。然而,在典型的正規表示式中,這些基本片段會使用組合運算子 STS|TS* 等組合成更複雜的模式(在這些範例中,"S""T" 是正規子表達式)。

此類組合可能包含選項,導致選擇問題:如果我們將正規表示式 a|ab"abc" 相符,它會與子字串 "a""ab" 相符嗎?一種描述實際上符合哪個子字串的方法是回溯概念(請參閱 "回溯")。然而,此描述層級太低,會讓您以特定實作的方式思考。

另一種描述從「較佳」/「較差」的概念開始。所有可能由給定的正規表示式符合的子字串都可以從「最佳」符合排序到「最差」符合,而會選取「最佳」符合。這以「哪個較佳?」的問題取代「選取哪個?」的問題,以及「哪些符合較佳,哪些較差?」的問題。

同樣地,對於基本片段來說沒有這樣的問題,因為在給定位置最多只可能有一個匹配。本節說明組合運算子的較好/較差概念。在以下說明中,"S""T" 是正規子表達式。

ST

考慮兩個可能的匹配,ABA'B'"A"A' 是可以由 "S" 匹配的子字串,"B"B' 是可以由 "T" 匹配的子字串。

如果 "A"A' 更適合與 "S" 匹配,則 ABA'B' 更適合匹配。

如果 "A"A' 相同:如果 "B"B' 更適合與 "T" 匹配,則 ABAB' 更適合匹配。

S|T

"S" 可以匹配時,它比只有 "T" 可以匹配時更適合匹配。

兩個 "S" 匹配的排序與 "S" 相同。兩個 "T" 匹配也類似。

S{REPEAT_COUNT}

匹配為 SSS...S(重複必要次數)。

S{min,max}

匹配為 S{max}|S{max-1}|...|S{min+1}|S{min}

S{min,max}?

匹配為 S{min}|S{min+1}|...|S{max-1}|S{max}

S?, S*, S+

分別與 S{0,1}S{0,BIG_NUMBER}S{1,BIG_NUMBER} 相同。

S??, S*?, S+?

分別與 S{0,1}?S{0,BIG_NUMBER}?S{1,BIG_NUMBER}? 相同。

(?>S)

匹配 "S" 的最佳匹配,而且只匹配該匹配。

(?=S), (?<=S)

只考慮 "S" 的最佳匹配。(這只有在 "S" 有擷取括號,且在整個正規表達式的其他地方使用反向參照時才重要。)

(?!S), (?<!S)

對於這個群組運算子,不需要說明排序,因為只有 "S" 是否可以匹配才重要。

(??{ EXPR }), (?PARNO)

排序與正規表達式相同,而正規表達式是 EXPR 的結果,或擷取群組 PARNO 包含的模式。

(?(condition)yes-pattern|no-pattern)

回想一下,實際上符合 yes-patternno-pattern 的哪一個已經確定。比對的順序與所選子表達式相同。

上述食譜描述了在 特定位置 的比對順序。還需要一個規則來了解如何為整個正規表示式確定比對:較早位置的比對總是優於較後位置的比對。

建立自訂 RE 引擎

從 Perl 5.10.0 開始,可以建立自訂正規表示式引擎。這不適合膽小的人,因為它們必須在 C 層級插入。請參閱 perlreapi 以取得更多詳細資料。

作為替代方案,重載常數(請參閱 overload)提供了一個簡單的方法來擴充 RE 引擎的功能,方法是將一個樣式替換為另一個樣式。

假設我們要啟用新的 RE 逸出序列 \Y|,它會在空白字元與非空白字元之間的邊界比對。請注意,(?=\S)(?<!\S)|(?!\S)(?<=\S) 會在這些位置完全比對,所以我們希望每個 \Y| 出現在較複雜版本的相同位置。我們可以建立一個模組 customre 來執行此操作

package customre;
use overload;

sub import {
  shift;
  die "No argument to customre::import allowed" if @_;
  overload::constant 'qr' => \&convert;
}

sub invalid { die "/$_[0]/: invalid escape '\\$_[1]'"}

# We must also take care of not escaping the legitimate \\Y|
# sequence, hence the presence of '\\' in the conversion rules.
my %rules = ( '\\' => '\\\\',
              'Y|' => qr/(?=\S)(?<!\S)|(?!\S)(?<=\S)/ );
sub convert {
  my $re = shift;
  $re =~ s{
            \\ ( \\ | Y . )
          }
          { $rules{$1} or invalid($re,$1) }sgex;
  return $re;
}

現在,use customre 在常數正規表示式中啟用新的逸出,,那些沒有任何執行時期變數內插的逸出。如 overload 中所述,此轉換只會在正規表示式的文字部分上執行。對於 \Y|$re\Y|,此正規表示式的變數部分需要明確轉換(但僅當 \Y| 的特殊意義應在 $re 內啟用時)。

use customre;
$re = <>;
chomp $re;
$re = customre::convert $re;
/\Y|$re\Y|/;

嵌入式程式碼執行頻率

(?{})(??{}) 在模式中執行的確切次數規則未指定,而 (*{}) 更是如此。在成功配對的情況下,您可以假設它們會 DWIM,並且會在模式的接受路徑中從左到右順序執行適當次數,就像任何其他元模式一樣。非接受路徑和配對失敗如何影響模式執行的次數並未明確指定,並且可能會根據可應用於模式的最佳化而有所不同,並且可能會隨著版本而改變。

例如在

"aaabcdeeeee"=~/a(?{print "a"})b(?{print "b"})cde/;

「a」或「b」列印的確切次數未指定為失敗,但您可以假設它們會在成功配對期間列印至少一次,此外,您可以假設如果列印「b」,它將會出現在至少一個「a」之後。

在以下的分支建構的情況下

/a(b|(?{ print "a" }))c(?{ print "c" })/;

您可以假設輸入「ac」將輸出「ac」,而「abc」將只輸出「c」。

當嵌入式程式碼被量化時,成功的配對將為量化器的每個配對反覆呼叫程式碼一次。例如

"good" =~ /g(?:o(?{print "o"}))*d/;

將輸出「o」兩次。

由於歷史和一致性的原因,在模式中的任何地方使用正常的程式碼區塊將會停用某些最佳化。從 5.37.7 開始,您可以使用「樂觀」程式碼區塊 (*{ ... }) 來取代 (?{ ... }),如果您 *不* 希望停用這些最佳化。這可能會導致程式碼區塊被呼叫的次數比沒有樂觀時少。

PCRE/Python 支援

從 Perl 5.10.0 開始,Perl 支援 regex 語法的幾個 Python/PCRE 特定擴充功能。雖然鼓勵 Perl 程式設計師使用 Perl 特定語法,但以下內容也可以接受

(?P<NAME>pattern)

定義一個命名擷取群組。等同於 (?<NAME>pattern)

(?P=NAME)

反向參照到一個命名擷取群組。等同於 \g{NAME}

(?P>NAME)

子常式呼叫到一個命名擷取群組。等同於 (?&NAME)

錯誤

Unicode 規則中關於不區分大小寫的配對有許多問題。請參閱上方 "修改器" 下的 "i"

本文件從難以理解到完全不透明。到處充滿術語的漫遊散文在幾個地方難以理解。

本文件需要重新編寫,將教學內容與參考內容分開。

另請參閱

Perl 模式配對中使用的模式語法演變自貝爾實驗室研究 Unix 第 8 版 (版本 8) regex 常式。 (程式碼實際上 (遠遠地) 衍生自 Henry Spencer 對那些 V8 常式的自由再實作。)

perlrequick.

perlretut.

perlop 中的「Regexp 引用式運算子」.

perlop 中的「引號建構的解析血腥細節」.

perlfaq6.

perlfunc 中的「pos」.

perllocale.

perlebcdic.

精通正規表示式,傑佛瑞·弗里德爾著,歐萊禮與合夥人出版。