一次性子組

對于同時有最大值和最小值量詞限制的重復(fù)項, 在匹配失敗后, 緊接著會以另外一個重復(fù)次數(shù)重新評估是否能使模式匹配。 當模式的作者明確知道執(zhí)行上沒有問題時, 通過改變匹配的行為或者使其更早的匹配失敗以阻止這種行為是很有用的。

考慮一個例子,模式 \d+foo 應(yīng)用到目標行 123456bar 時:

在匹配了 6 個數(shù)字后匹配 ”foo” 時失敗,通常的行為時匹配器嘗試使 \d+ 只匹配 5 個數(shù)字, 只匹配 4 個數(shù)字,在最終失敗之前依次進行嘗試。 一次性子組提供了一種特殊的意義, 當模式的一部分得到匹配后,不再對其進行重新評估, 因此匹配器在第一次匹配 ”foo” 失敗后就能立刻失敗。語法符號是另外一種特殊的括號, 以 (?> 開始,比如 (?>\d+)bar

這種括號對模式的一部分提供了”鎖定”,當它包含一個匹配之后, 會阻止未來模式失敗后對它內(nèi)部的后向回溯。后向回溯在這里失效, 其他工作照常進行。

換一種說法,如果在目標字符串中當前匹配點是錨點, 這種類型的子組匹配的字符串等同于一個獨立的模式匹配。

一次性子組不是捕獲子組。如上面的例子,簡單而言, 就是盡其所能吃掉盡可能多的匹配字符。因此, 盡管 \d+ 和 \d+? 都會調(diào)整要匹配的數(shù)字的個數(shù)以便模式的其他部分匹配, (?>\d+) 卻僅能匹配整個數(shù)字序列。

這個(語法)結(jié)構(gòu)可以包含任意復(fù)雜度的字符, 也可以嵌套。

一次性子組可以和后瞻斷言結(jié)合使用來指定在目標字符串末尾的有效匹配。 考慮當一個簡單的模式比如 abcd$ 應(yīng)用到一個不匹配的長字符串上。 由于匹配時從左到右處理的, PCRE會從目標中查找每一個 ”a” 然后查看是否緊接著會匹配模式的剩余部分。 如果模式是 ^.*abcd$ , 那么初始的 .* 將首先匹配整個字符串,但是當它失敗后(因為緊接著不是 ”a”), 它會回溯所有的匹配,依次吐出最后 1 個字符,倒數(shù)第 2 個字符等等。 從右向左查找整個字符串中的 ”a”, 因此,我們不能很好的退出。然而, 如果模式寫作 ^(?>.*)(?<=abcd) 那么它就不會回溯 .* 這一部分, 它僅僅用于匹配整個字符串。后瞻斷言對字符串末尾的后四個字符做了一個測試。 如果它失敗,匹配立即失敗。對于長字符串, 這個模式將會帶來顯著的處理時間上的性能提升。

當一個模式中包含一個子組自己可以無限重復(fù)并且內(nèi)部有無限重復(fù)元素時, 使用一次性子組是避免一些失敗匹配消耗大量時間的唯一途徑。 模式 (\D+|<\d+>)*[!?] 匹配一個不限制數(shù)目的非數(shù)字字符或由 <> 閉合的數(shù)字字符緊跟著 ! 或 ?。 當它匹配的時候,運行時快速的。然而, 如果它應(yīng)用到 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 上將會在報告錯誤之前消耗很多時間。 這是因為字符串可以用于兩種重復(fù)規(guī)則,并且需要為兩種重復(fù)規(guī)則都分配進行嘗試。 (示例的結(jié)尾使用 [!?] 而不是單個的字符, 是因為 PCRE 和 perl 都會對模式最后是一個單獨字符時的快速報錯有優(yōu)化。 它們會記錄最后需要匹配的單個字符,當它們沒有出現(xiàn)在字符串中時快速報錯。) 如果模式修改為 ((?>\D+)|<\d+>)*[!?] 就會快速得到報錯。(譯注: 對于這里給出的模式,當目標字符串更長的時候,消耗時間會迅速增加,慎用。)