本網站更多內容 |
簡介 |
正規表示式快速入門 |
正規表示式教學 |
替換字串教學 |
應用程式和語言 |
正規表示式範例 |
正規表示式參考 |
替換字串參考 |
書籍評論 |
可列印 PDF |
關於本網站 |
RSS 饋送和部落格 |
Perl 5.10、PCRE 4.0、Ruby 2.0,以及這三個版本的後續版本都支援正規表示式遞迴。Perl 使用語法 (?R),並以 (?0) 作為同義詞。Ruby 2.0 使用 \g<0>。PCRE 在 7.7 版開始支援所有三個語法。較早的版本只支援 Perl 語法(Perl 實際上是從 PCRE 複製而來)。Delphi、PHP 和 R 的最新版本也支援所有三個語法,因為它們的正規表示式函式是以 PCRE 為基礎。 JGsoft V2 也支援正規表示式遞迴的所有變體。
雖然 Ruby 1.9 沒有正規表示式遞迴的語法,但它支援 擷取群組遞迴。因此,如果您將整個正規表示式包在一個擷取群組中,您可以在 Ruby 1.9 中遞迴整個正規表示式。 .NET 不支援遞迴,但它支援 平衡群組,可以用來取代遞迴,以比對平衡結構。
正如我們稍後將看到的,在遞迴期間,Perl、PCRE 和 Ruby 處理 反向引用 和 回溯 的方式有所不同。儘管它們複製了彼此的語法,但它們並未複製彼此的行為。然而,JGsoft V2 複製了它們的語法和行為。因此,JGsoft V2 有三種不同的方式來執行正規表示式遞迴,您可以使用不同的語法進行選擇。但這些差異不會出現在此頁面的基本範例中。
Boost 1.42 從 Perl 複製了語法。但它的實作被錯誤破壞了。Boost 1.60 嘗試修正 遞迴量詞 的行為,但它仍然與其他版本有很大的不同,並且與 Boost 的先前版本不相容。Boost 1.64 最終停止在 無限遞迴 時崩潰。但整個正規表示式的遞迴仍然只嘗試第一個選項。
正規表示式 a(?R)?z、a(?0)?z 和 a\g<0>?z 都匹配一個或多個字母 a,後跟完全相同的數量的字母 z。由於這些正規表示式在功能上是相同的,因此我們將使用帶有 R 的遞迴語法來查看此正規表示式如何匹配字串 aaazzz。
首先,a 匹配字串中的第一個 a。然後 regex 引擎到達 (?R)。這會告訴引擎在字串中的目前位置再次嘗試整個 regex。現在,a 匹配字串中的第二個 a。引擎再次到達 (?R)。在第二次遞迴中,a 匹配第三個 a。在第三次遞迴中,a 無法匹配字串中的第一個 z。這會導致 (?R) 失敗。但 regex 使用量詞讓 (?R) 可選。因此,引擎繼續處理 z,它會匹配字串中的第一個 z。
現在,regex 引擎已到達 regex 的結尾。但由於它在遞迴中已深入兩層,因此尚未找到整體匹配。它只找到 (?R) 的匹配。在成功匹配後退出遞迴,引擎也會到達 z。它現在匹配字串中的第二個 z。引擎在遞迴中仍深入一層,它會帶著成功匹配退出。最後,z 匹配字串中的第三個 z。引擎再次到達 regex 的結尾。這次,它不在任何遞迴中。因此,它傳回 aaazzz 作為整體 regex 匹配。
遞迴的主要目的是匹配平衡結構或巢狀結構。通用 regex 為 b(?:m|(?R))*e,其中 b 是結構的開頭,m 是結構中間可能出現的內容,而 e 是結構結尾可能出現的內容。為了得到正確的結果,b、m 和 e 中的兩個不應能匹配相同的文字。你可以使用 原子群組 來取代 非擷取群組 以提升效能:b(?>m|(?R))*e。
實際應用中,常見的範例是配對一組平衡的括號。 \((?>[^()]|(?R))*\) 配對一對括號,中間可以包含任何文字,包含無限個括號,只要它們都配對正確。如果主旨字串包含不平衡的括號,第一個正規表示式配對是從最左邊開始的一對平衡括號,它可能出現在不平衡的開啟括號之後。如果你想要一個正規表示式,在包含不平衡括號的字串中找不到任何配對,那麼你需要使用 子常式呼叫 來取代遞迴。如果你想要找到一連串多對平衡括號,並將它們視為單一配對,你也需要一個子常式呼叫。
如果平衡結構中間可能出現的內容,也可能在沒有開頭和結尾的情況下單獨出現,那麼通用的正規表示式為 b(?R)*e|m。同樣地,b、m 和 e 都需要是互斥的。 \((?R)*\)|[^()]+ 配對一對平衡括號,就像前一節的正規表示式。但它也配對任何完全不包含括號的文字。
此正規表示式在 Boost 中無法正確運作。如果正規表示式有 交替 而非在群組內,則 Boost 中的整個正規表示式遞迴只會嘗試第一個交替。因此,在 Boost 中 \((?R)*\)|[^()]+ 會比對任意數量的平衡括號,其任意深度嵌套且中間沒有文字,或完全不包含任何括號的文字。如果您翻轉交替,則 [^()]+|\((?R)*\) 在 Boost 中會比對不含任何括號的文字,或一對括號,其間包含不含括號的文字。在其他所有版本中,這兩個正規表示式會找到相同的比對結果。
Boost 的解決方案是將交替放入群組中。 (?:\((?R)*\)|[^()]+) 和 (?:[^()]+|\((?R)*\)) 會在支援遞迴的本教學課程中討論的所有版本中找到相同的比對結果。
| 快速入門 | 教學課程 | 工具和語言 | 範例 | 參考 | 書籍評論 |
| 簡介 | 目錄 | 特殊字元 | 非列印字元 | Regex 引擎內部 | 字元類別 | 字元類別減法 | 字元類別交集 | 簡寫字元類別 | 點 | 錨點 | 字詞邊界 | 交替 | 可選項目 | 重複 | 群組和擷取 | 反向參照 | 反向參照,第 2 部分 | 命名群組 | 相對反向參照 | 分支重設群組 | 自由間距和註解 | Unicode | 模式修改器 | 原子群組 | 獨佔量詞 | 前瞻和後顧 | 前瞻和後顧,第 2 部分 | 將文字保留在比對之外 | 條件式 | 平衡群組 | 遞迴 | 子常式 | 無限遞迴 | 遞迴和量詞 | 遞迴和擷取 | 遞迴和反向參照 | 遞迴和回溯 | POSIX 方括號表示式 | 零長度比對 | 繼續比對 |
頁面網址:https://regular-expressions.dev.org.tw/recurse.html
頁面最後更新時間:2019 年 11 月 22 日
網站最後更新時間:2024 年 3 月 15 日
版權所有 © 2003-2024 Jan Goyvaerts。保留所有權利。