快速入門
教學課程
工具和語言
範例
參考
書籍評論
正規表示式教學課程
簡介
目錄
特殊字元
不可列印字元
正規表示式引擎內部
字元類別
字元類別減法
字元類別交集
簡寫字元類別
錨點
字詞邊界
交替
選擇性項目
重複
群組和擷取
反向參照
反向參照,第 2 部分
命名群組
相對反向參照
分支重設群組
自由間距和註解
Unicode
模式修改器
原子群組
獨佔量詞
前瞻和後顧
環顧,第 2 部分
將文字保留在比對之外
條件式
平衡群組
遞迴
子常式
無限遞迴
遞迴和量詞
遞迴和擷取
遞迴和反向參照
遞迴和回溯
POSIX 方括號表示式
零長度比對
繼續比對
本網站的更多內容
簡介
正規表示式快速入門
正規表示式教學課程
替換字串教學課程
應用程式和語言
正規表示式範例
正規表示式參考
替換字串參考
書籍評論
可列印 PDF
關於本網站
RSS 饋送和部落格
RegexBuddy—Better than a regular expression tutorial!

指定遞迴層級的反向參照

本教學課程中較早的主題說明了 正規表示式遞迴正規表示式子常式。在本主題中,「遞迴」一詞指的是整個正規表示式的遞迴、擷取群組的遞迴,以及對擷取群組的子常式呼叫。前一個主題也說明了這些功能在 Ruby 中 處理擷取群組的方式不同於 Perl 和 PCRE。

Perl、PCRE 和 Boost 在退出遞迴時會還原擷取群組。這表示 Perl、PCRE 和 Boost 中的反向參照會比對由相同遞迴層級的擷取群組比對的相同文字。這使得可以執行諸如 比對迴文 等動作。

Ruby 在退出遞迴時不會還原擷取群組。一般反向參照 會比對與未回溯的擷取群組最近一次比對相同的文字,而不論擷取群組是在與反向參照相同或不同的遞迴層級中找到比對。基本上,Ruby 中的一般反向參照不會注意遞迴。

但是,儘管 Ruby 中的一般擷取群組儲存不會對遞迴有特別處理,但 Ruby 實際上會為所有遞迴層級的每個擷取群組儲存完整的比對堆疊。此堆疊甚至包含正規表示式引擎已退出的遞迴層級。

Ruby 中的反向參照可以比對與在反向參照評估時相對於其遞迴層級的任何遞迴層級的擷取群組比對相同的文字。您可以使用 命名反向參照 的相同語法執行此動作,方法是在名稱後加上符號和數字。在大部分情況下,您會使用 +0 來指定您要反向參照在相同遞迴層級的擷取群組中重複使用文字。您可以指定正數來參照較深層遞迴層級的擷取群組。這會是正規表示式引擎已退出的遞迴。您可以指定負數來參照較淺層級的擷取群組。這會是仍在進行中的遞迴。

JGsoft V2 也支援使用與 Ruby 相同語法指定遞迴層級的反向參照。若要讓 JGsoft V2 產生與 Ruby 相同的行為,您必須對子常式呼叫使用 Ruby 的 \g 語法。

Ruby 中的奇數長度迴文

在 Ruby 中,您可以使用 \b(?'word'(?'letter'[a-z])\g'word'\k'letter+0'|[a-z])\b 來比對迴文單字,例如 adadradarracecarredivider。為了讓此範例保持簡潔,此正規表示式只會比對長度為奇數個字母的迴文單字。

讓我們看看此正規表示式如何比對 radar字詞邊界 \b 比對字串的開頭。正規表示式引擎會進入擷取群組「word」。[a-z] 比對 r,然後將其儲存在擷取群組「letter」的堆疊中,遞迴層級為 0。現在,正規表示式引擎會進入群組「word」的第一個遞迴。(?'letter'[a-z]) 比對並擷取 a,遞迴層級為 1。正規表示式會進入群組「word」的第二個遞迴。(?'letter'[a-z]) 擷取 d,遞迴層級為 2。在接下來的兩個遞迴中,群組會擷取 ar,層級分別為 3 和 4。第五個遞迴會失敗,因為字串中沒有字元可供 [a-z] 比對。正規表示式引擎必須回溯。

現在,正規表示式引擎必須嘗試群組「word」內的第二個選項。正規表示式中第二個 [a-z] 比對字串中的最後一個 r。現在,引擎會退出成功的遞迴,回到第三個遞迴,向上提升一層。

比對 \g'word' 之後,引擎會到達 \k'letter+0'。反向參照會失敗,因為正規表示式引擎已經到達主旨字串的結尾。因此,它會再次回溯。第二個選項現在會比對 a。正規表示式引擎會退出第三個遞迴。

正規表示式引擎再次比對 \g'word',需要再次嘗試反向參照。反向參照指定 +0 或目前的遞迴層級,也就是 2。在此層級,擷取群組比對 d。反向參照會失敗,因為字串中的下一個字元是 r。再次回溯,第二個選項會比對 d

現在,\k'letter+0' 會比對字串中的第二個 a。這是因為正規表示式引擎已回到第一個遞迴,其中擷取群組比對到第一個 a。正規表示式引擎會離開第一個遞迴。

正規表示式引擎現在已回到所有遞迴之外。在此層級,擷取群組儲存 r。反向參照現在可以比對字串中的最後一個 r。由於引擎不再位於任何遞迴中,因此會繼續處理群組之後的正規表示式剩餘部分。\b 會比對字串的結尾。正規表示式已到達結尾,並傳回 radar 作為整體比對結果。

反向參照至其他遞迴層級

如果修改我們的迴文範例,就能輕鬆了解反向參照至其他遞迴層級。 abcdefedcba 也是一個迴文,可以透過先前的正規表示式比對。考慮正規表示式 \b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter-1'|z)|[a-z])\b。反向參照現在想要比對擷取群組堆疊中較低一層的文字。它會與字母 z 交替,以便在反向參照無法比對時,可以比對某些內容。

新的正規表示式會比對 abcdefdcbaz 等內容。經過大量比對和回溯後,第二個 [a-z] 會比對 f。正規表示式引擎會離開成功的第五個遞迴。擷取群組「letter」已儲存遞迴層級 0 到 4 中的比對結果 abcde。該群組的其他比對結果已回溯,因此未保留。

現在,引擎會評估反向參照 \k'letter-1'。目前的層級為 4,而反向參照指定 -1。因此,引擎會嘗試比對 d,並成功。引擎會離開第四個遞迴。

反向參照會繼續比對 cba,直到正規表示式引擎離開第一個遞迴。現在,在所有遞迴之外,正規表示式引擎再次到達 \k'letter-1'。目前的層級為 0,而反向參照指定 -1。由於遞迴層級 -1 從未發生,因此反向參照無法比對。這不是錯誤,而只是 反向參照至未參與的擷取群組。但反向參照有另一個選項。z 會比對 z,而 \b 會比對字串的結尾。abcdefdcbaz 已成功比對。

您可以盡可能地使用它。正規表示式 \b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter-2'|z)|[a-z])\b 符合 abcdefcbazz\b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter-99'|z)|[a-z])\b 符合 abcdefzzzzzz

朝相反的方向進行,\b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter+1'|z)|[a-z])\b 符合 abcdefzedcb。同樣地,在進行大量配對和回溯之後,第二個 [a-z] 符合 f,正規表示式引擎回到遞迴層級 4,而群組「letter」在遞迴層級 0 到 4 的堆疊中包含 abcde

現在,引擎評估反向參照 \k'letter+1'。目前的層級是 4,而反向參照指定 +1。擷取群組在遞迴層級 5 進行回溯。這表示我們有一個反向參照指向一個非參與群組,而無法配對。替代的 z 符合。引擎退出第四個遞迴。

在遞迴層級 3,反向參照指向遞迴層級 4。由於擷取群組在遞迴層級 4 成功配對,因此它仍然在堆疊中保留該配對,即使正規表示式引擎已經退出該遞迴。因此,\k'letter+1' 符合 e。遞迴層級 3 成功退出。

反向參照繼續配對 dc,直到正規表示式引擎退出第一個遞迴。現在,在所有遞迴之外,正規表示式引擎再次到達 \k'letter+1'。目前的層級是 0,而反向參照指定 +1。擷取群組仍然保留所有先前成功的遞迴層級。因此,反向參照仍然可以配對群組在第一個遞迴中擷取的 b。現在,\b 在字串的結尾配對。成功配對 abcdefzdcb

您也可以朝這個方向盡情發揮。正規表示式 \b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter+2'|z)|[a-z])\b 符合 abcdefzzedc\b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter+99'|z)|[a-z])\b 符合 abcdefzzzzzz