递归和子常式调用可能不是原子的递归和子常式调用可能不是原子的递归和子常式调用可能不是原子的递归和子常式调用可能不是原子的
  • 文章
  • 正则表达式
    • 工具
  • 登录
找到的结果: {phrase} (显示: {results_count} 共: {results_count_total})
显示: {results_count} 共: {results_count_total}

加载更多搜索结果...

搜索范围
模糊匹配
搜索标题
搜索内容
发表 admin at 2024年3月5日
类别
  • 正则表达式
标签
递归和子常式调用可能不是原子的
  • 简
  • 繁
  • En
关于正则表达式 » 正则表达式教程 » 递归和子常式调用可能不是原子的

正则表达式教程
简介
目录
特殊字符
不可打印字符
正则表达式引擎内部
字符类别
字符类别减法
字符类别交集
简写字符类别
点
锚点
字词边界
交替
选用项目
重复
群组和截取
反向引用
反向引用,第 2 部分
命名组
相对反向引用
分支重设群组
自由间距和注解
Unicode
模式修改器
原子组
独占量词
前瞻和后顾
环顾,第 2 部分
将文本保留在比对之外
条件式
平衡组
递归
子常式
无限递归
递归和量词
递归和截取
递归和反向引用
递归和回溯
POSIX 方括号表达式
零长度比对
继续比对
此网站上的更多信息
简介
正则表达式快速入门
正则表达式教程
替换字符串教程
应用程序和语言
正则表达式范例
正则表达式参考
替换字符串参考

递归和子常式调用可能不是原子的

本教程中较早的主题说明了 正则表达式递归 和 正则表达式子常式。在本主题中,「递归」一词是指整个正则表达式的递归、捕获组的递归,以及对捕获组的子常式调用。

Perl 和 Ruby 会在递归后的正则表达式其余部分失败时回溯到递归中。它们会视需要尝试递归的所有排列,以允许正则表达式其余部分比对。PCRE 会将递归视为 原子。PCRE 会在递归期间正常回溯,但一旦递归比对成功,即使正则表达式其余部分无法比对,它也不会尝试递归的任何进一步排列。结果是 Perl 和 Ruby 可能会找到 PCRE 找不到的正则表达式比对,或者 Perl 和 Ruby 可能会找到不同的正则表达式比对。

考虑 Perl 中的正则表达式 aa$|a(?R)a|a 或 Ruby 2.0 中的等效表达式 aa$|a\g'0'a|a。PCRE 支持两种语法。让我们看看 Perl、Ruby 和 PCRE 如何进行此正则表达式的配对进程,其中 aaa 是主旨字符串。

第一个选项 aa$ 失败,因为 锚点 无法在字符串中第二和第三个 a 之间配对。尝试字符串开头的第二个选项,a 配对 a。现在正则表达式引擎进入第一次递归。

在递归内,第一个选项配对字符串中第二和第三个 a。正则表达式引擎退出成功的递归。但是现在,正则表达式中 (?R) 或 \g'0' 后面的 a 无法配对,因为正则表达式引擎已经到达字符串的结尾。因此,正则表达式引擎必须回溯。这里是 PCRE 与 Perl 或 Ruby 行为不同的部分。

Perl 和 Ruby 记得在递归内正则表达式配对第二个选项,并且有三个可能的选项。Perl 和 Ruby 回溯进入递归。递归内的第二个选项回溯,将目前的配对缩减为字符串中的第一个 a。现在尝试第三个选项。a 配对字符串中的第二个 a。正则表达式引擎再次成功退出相同的递归。这次,正则表达式中 (?R) 或 \g'0' 后面的 a 配对字符串中的第三个 a。aaa 被发现为整体配对。

另一方面,PCRE 除了匹配字符串结尾的 aa 之外,不会记住任何递归。PCRE 会在递归中进行回溯,将目前的匹配范围缩小到字符串中的第一个 a。但这会让正则表达式中的第二个选项没有任何进一步的排列组合可以尝试。因此,第二个选项开头的 a 也会进行回溯,将目前的匹配范围缩小到没有任何内容。PCRE 会从字符串开头使用第三个选项继续尝试匹配,并发现 a 匹配字符串开头的 a。在 PCRE 中,这便是整体匹配。

您可以在 Perl 和 Ruby 中通过添加原子组来让递归具有原子性。Perl 中的 aa$|a(?>(?R))a|a 和 Ruby 中的 aa$|a(?>\g'0')a|a 与 PCRE 中的原始正则表达式相同。

Boost 有两种心态。Boost 中整个正则表达式的递归是原子的,就像在 PCRE 中一样。但 Boost 会像 Perl 一样回溯到子常式调用和捕获组的递归中。因此,您可以通过将整个正则表达式包装到捕获组中,然后调用它来在 Boost 中进行非原子递归。

PCRE2 最初的行为就像 PCRE,将所有递归和子常式调用视为原子。PCRE2 10.30 更改了这一点,试图更像 Perl,但最终却像 Boost。PCRE2 10.30 会像 Perl 一样回溯到子常式调用和捕获组的递归中。但 PCRE2 仍然无法回溯到整个正则表达式的递归中。在以下范例中,「PCRE」仅表示原始的 PCRE。对于 PCRE2 10.22 及更早版本,请遵循 PCRE 范例。对于 PCRE2 10.30 及更新版本,请遵循 Perl 范例。

Perl 和 Ruby 中任意长度的回文

关于 递归和捕获组 的主题说明了一个正则表达式,用于比对长度为奇数个字符的 回文。这个解法看起来很简单。在 Perl 中,\b(?'word'(?'letter'[a-z])(?&word)\k'letter'|[a-z]?)\b 就可达成目标。量词 ? 使得比对回文中间字符的 [a-z] 为选用。在 Ruby 中,我们可以使用 \b(?'word'(?'letter'[a-z])\g'word'\k'letter+0'|[a-z]?)\b,它将相同的量词添加到解法中,用来指定 反向引用的递归层级。在 PCRE 中,Perl 解法仍比对奇数长度的回文,但无法比对偶数长度的回文。

让我们看看这些正则表达式如何比对或无法比对 deed。PCRE 的开头与 Perl 和 Ruby 相同,就像原始正则表达式一样。群组「letter」比对 d。在连续三次递归期间,群组截取 e、e 和 d。第四次递归失败,因为没有字符可以比对。回到第三次递归,第一个选项会回溯,而第二个选项比对字符串结尾的 d。引擎以成功的比对结束第三次递归。回到第二次递归,反向引用会失败,因为字符串中没有字符了。

在这里,行为出现分歧。Perl 和 Ruby 会回溯进入第三次递归,并回溯量词 ?,这会让第二个选项变为可选。在第三次递归中,第二个选项会放弃字符串结尾处比对到的 d。引擎再次退出第三次递归,这次是成功比对到长度为 0 的字符串。回到第二次递归,反向引用仍然会失败,因为群组保存了第二次递归的 e,但字符串中的下一个字符是 d。因此,第一个选项会被回溯,而第二个选项会比对到字符串中的第二个 e。第二次递归成功退出。

在第一次递归中,反向引用再次失败。群组保存了第一次递归的 e,但字符串中的下一个字符是 d。同样地,Perl 和 Ruby 会回溯进入第二次递归,尝试第二个选项找到长度为 0 的比对。回到第一次递归,反向引用现在比对到字符串中的第二个 e。引擎成功离开第一次递归。回到整体比对尝试,反向引用比对到字符串中的最后一个 d。前缀后缀界线成功,找到整体比对。

然而,PCRE 不会回溯进入第三次递归。当它回溯第二次递归中的第一个选项时,它会回溯越过第三次递归。现在,第二次递归中的第二个选项会比对到字符串中的第二个 e。第二次递归成功退出。

在第一次递归中,反向引用再次失败。群组保存了第一次递归的 e,但字符串中的下一个字符是 d。同样地,PCRE 不会回溯进入第二次递归,而是立即让第一次递归中的第一个选项失败。现在,第一次递归中的第二个选项会比对到字符串中的第一个 e。PCRE 成功退出第一次递归。回到整体比对尝试,反向引用会失败,因为群组在递归之前截取了 d,而下一个字符是字符串中的第二个 e。再次回溯,整体正则表达式比对中的第二个选项现在会比对到字符串中的第一个 d。然后,前缀后缀界线会失败。PCRE 没有找到任何比对。

PCRE 中任何长度的回文

若要在 PCRE 中比对任何长度的回文,我们需要一个正则表达式,分别比对偶数个字符和奇数个字符的字词。自由间距模式让这个正则表达式更容易阅读

\b(?'word'
  
(?'oddword' (?'oddletter' [a-z])(?P>oddword)  \k'oddletter' |[a-z])
| (?'evenword'(?'evenletter'[a-z])(?P>evenword)?\k'evenletter')
)\b

基本上,这是两个原始正则表达式的拷贝与交替组合。第一个交替选项将群组「word」和「letter」重命名为「oddword」和「oddletter」。第二个交替选项将群组「word」和「letter」重命名为「evenword」和「evenletter」。调用 (?P>evenword) 现在使用问号,而不是交替选项 |[a-z],变成可选的。新的群组「word」结合了两个群组「oddword」和「evenword」,以便字符边界仍适用于整个正则表达式。

此正则表达式中的第一个交替选项「oddword」与主题中讨论的正则表达式以完全相同的方式,比对出奇数长度的回文,例如 radar。递归和捕获组。新正则表达式中的第二个交替选项从未尝试过。

当字符串是偶数长度的回文时,例如 deed,新正则表达式会先尝试第一个交替选项的所有排列组合。仅在第一个交替选项找不到比对项时,才会尝试第二个交替选项「evenword」。

第二个替代方案的开头与原始正则表达式相同。群组「evenletter」比对 d。在连续三次递归中,群组截取 e、e 和 d。第四次递归失败,因为没有字符可以比对。回到第三次递归,正则表达式引擎会注意到递归调用 (?P>evenword)? 是选用的。它会继续进行反向引用 \k'evenletter'。反向引用会失败,因为字符串中没有任何字符。由于递归没有其他替代方案可以尝试,因此会回溯。群组「evenletter」必须放弃最近的比对,而 PCRE 会退出失败的第三次递归。

在第二次递归中,反向引用会失败,因为捕获组在该递归中比对 e,但字符串中的下一个字符是 d。群组会放弃另一个比对,而 PCRE 会退出失败的第二次递归。

回到第一次递归,反向引用会成功。群组在该递归中比对字符串中的第一个 e,而反向引用会比对第二个。PCRE 会退出成功的第一次递归。

回到整体比对尝试中,反向引用会再次成功。群组在整体比对尝试中比对字符串开头的 d,而反向引用会比对最后一个 d。退出群组「evenword」和「word」,字词边界会比对字符串的结尾。 deed 是整体比对。

遞迴和子常式呼叫可能不是原子的
  • 简
  • 繁
  • En
關於正規表示式 » 正規表示式教學 » 遞迴和子常式呼叫可能不是原子的

正規表示式教學
簡介
目錄
特殊字元
不可列印字元
正規表示式引擎內部
字元類別
字元類別減法
字元類別交集
簡寫字元類別
點
錨點
字詞邊界
交替
選用項目
重複
群組和擷取
反向參照
反向參照,第 2 部分
命名群組
相對反向參照
分支重設群組
自由間距和註解
Unicode
模式修改器
原子群組
獨佔量詞
前瞻和後顧
環顧,第 2 部分
將文字保留在比對之外
條件式
平衡群組
遞迴
子常式
無限遞迴
遞迴和量詞
遞迴和擷取
遞迴和反向參照
遞迴和回溯
POSIX 方括號表示式
零長度比對
繼續比對
此網站上的更多資訊
簡介
正則表示式快速入門
正則表示式教學
替換字串教學
應用程式和語言
正則表示式範例
正則表示式參考
替換字串參考

遞迴和子常式呼叫可能不是原子的

本教學課程中較早的主題說明了 正則表示式遞迴 和 正則表示式子常式。在本主題中,「遞迴」一詞是指整個正規表示式的遞迴、擷取群組的遞迴,以及對擷取群組的子常式呼叫。

Perl 和 Ruby 會在遞迴後的正規表示式其餘部分失敗時回溯到遞迴中。它們會視需要嘗試遞迴的所有排列,以允許正規表示式其餘部分比對。PCRE 會將遞迴視為 原子。PCRE 會在遞迴期間正常回溯,但一旦遞迴比對成功,即使正規表示式其餘部分無法比對,它也不會嘗試遞迴的任何進一步排列。結果是 Perl 和 Ruby 可能會找到 PCRE 找不到的正規表示式比對,或者 Perl 和 Ruby 可能會找到不同的正規表示式比對。

考慮 Perl 中的正規表示式 aa$|a(?R)a|a 或 Ruby 2.0 中的等效表示式 aa$|a\g'0'a|a。PCRE 支援兩種語法。讓我們看看 Perl、Ruby 和 PCRE 如何進行此正規表示式的配對程序,其中 aaa 是主旨字串。

第一個選項 aa$ 失敗,因為 錨點 無法在字串中第二和第三個 a 之間配對。嘗試字串開頭的第二個選項,a 配對 a。現在正規表示式引擎進入第一次遞迴。

在遞迴內,第一個選項配對字串中第二和第三個 a。正規表示式引擎退出成功的遞迴。但是現在,正規表示式中 (?R) 或 \g'0' 後面的 a 無法配對,因為正規表示式引擎已經到達字串的結尾。因此,正規表示式引擎必須回溯。這裡是 PCRE 與 Perl 或 Ruby 行為不同的部分。

Perl 和 Ruby 記得在遞迴內正規表示式配對第二個選項,並且有三個可能的選項。Perl 和 Ruby 回溯進入遞迴。遞迴內的第二個選項回溯,將目前的配對縮減為字串中的第一個 a。現在嘗試第三個選項。a 配對字串中的第二個 a。正規表示式引擎再次成功退出相同的遞迴。這次,正規表示式中 (?R) 或 \g'0' 後面的 a 配對字串中的第三個 a。aaa 被發現為整體配對。

另一方面,PCRE 除了匹配字串結尾的 aa 之外,不會記住任何遞迴。PCRE 會在遞迴中進行回溯,將目前的匹配範圍縮小到字串中的第一個 a。但這會讓正規表示式中的第二個選項沒有任何進一步的排列組合可以嘗試。因此,第二個選項開頭的 a 也會進行回溯,將目前的匹配範圍縮小到沒有任何內容。PCRE 會從字串開頭使用第三個選項繼續嘗試匹配,並發現 a 匹配字串開頭的 a。在 PCRE 中,這便是整體匹配。

您可以在 Perl 和 Ruby 中透過新增原子群組來讓遞迴具有原子性。Perl 中的 aa$|a(?>(?R))a|a 和 Ruby 中的 aa$|a(?>\g'0')a|a 與 PCRE 中的原始正規表示式相同。

Boost 有兩種心態。Boost 中整個正規表示式的遞迴是原子的,就像在 PCRE 中一樣。但 Boost 會像 Perl 一樣回溯到子常式呼叫和擷取群組的遞迴中。因此,您可以透過將整個正規表示式包裝到擷取群組中,然後呼叫它來在 Boost 中進行非原子遞迴。

PCRE2 最初的行為就像 PCRE,將所有遞迴和子常式呼叫視為原子。PCRE2 10.30 更改了這一點,試圖更像 Perl,但最終卻像 Boost。PCRE2 10.30 會像 Perl 一樣回溯到子常式呼叫和擷取群組的遞迴中。但 PCRE2 仍然無法回溯到整個正規表示式的遞迴中。在以下範例中,「PCRE」僅表示原始的 PCRE。對於 PCRE2 10.22 及更早版本,請遵循 PCRE 範例。對於 PCRE2 10.30 及更新版本,請遵循 Perl 範例。

Perl 和 Ruby 中任意長度的迴文

關於 遞迴和擷取群組 的主題說明了一個正規表示式,用於比對長度為奇數個字元的 迴文。這個解法看起來很簡單。在 Perl 中,\b(?'word'(?'letter'[a-z])(?&word)\k'letter'|[a-z]?)\b 就可達成目標。量詞 ? 使得比對迴文中間字元的 [a-z] 為選用。在 Ruby 中,我們可以使用 \b(?'word'(?'letter'[a-z])\g'word'\k'letter+0'|[a-z]?)\b,它將相同的量詞新增到解法中,用來指定 反向參照的遞迴層級。在 PCRE 中,Perl 解法仍比對奇數長度的迴文,但無法比對偶數長度的迴文。

讓我們看看這些正規表示式如何比對或無法比對 deed。PCRE 的開頭與 Perl 和 Ruby 相同,就像原始正規表示式一樣。群組「letter」比對 d。在連續三次遞迴期間,群組擷取 e、e 和 d。第四次遞迴失敗,因為沒有字元可以比對。回到第三次遞迴,第一個選項會回溯,而第二個選項比對字串結尾的 d。引擎以成功的比對結束第三次遞迴。回到第二次遞迴,反向參照會失敗,因為字串中沒有字元了。

在這裡,行為出現分歧。Perl 和 Ruby 會回溯進入第三次遞迴,並回溯量詞 ?,這會讓第二個選項變為可選。在第三次遞迴中,第二個選項會放棄字串結尾處比對到的 d。引擎再次退出第三次遞迴,這次是成功比對到長度為 0 的字串。回到第二次遞迴,反向參照仍然會失敗,因為群組儲存了第二次遞迴的 e,但字串中的下一個字元是 d。因此,第一個選項會被回溯,而第二個選項會比對到字串中的第二個 e。第二次遞迴成功退出。

在第一次遞迴中,反向參照再次失敗。群組儲存了第一次遞迴的 e,但字串中的下一個字元是 d。同樣地,Perl 和 Ruby 會回溯進入第二次遞迴,嘗試第二個選項找到長度為 0 的比對。回到第一次遞迴,反向參照現在比對到字串中的第二個 e。引擎成功離開第一次遞迴。回到整體比對嘗試,反向參照比對到字串中的最後一個 d。字首字尾界線成功,找到整體比對。

然而,PCRE 不會回溯進入第三次遞迴。當它回溯第二次遞迴中的第一個選項時,它會回溯越過第三次遞迴。現在,第二次遞迴中的第二個選項會比對到字串中的第二個 e。第二次遞迴成功退出。

在第一次遞迴中,反向參照再次失敗。群組儲存了第一次遞迴的 e,但字串中的下一個字元是 d。同樣地,PCRE 不會回溯進入第二次遞迴,而是立即讓第一次遞迴中的第一個選項失敗。現在,第一次遞迴中的第二個選項會比對到字串中的第一個 e。PCRE 成功退出第一次遞迴。回到整體比對嘗試,反向參照會失敗,因為群組在遞迴之前擷取了 d,而下一個字元是字串中的第二個 e。再次回溯,整體正規表示式比對中的第二個選項現在會比對到字串中的第一個 d。然後,字首字尾界線會失敗。PCRE 沒有找到任何比對。

PCRE 中任何長度的迴文

若要在 PCRE 中比對任何長度的迴文,我們需要一個正規表示式,分別比對偶數個字元和奇數個字元的字詞。自由間距模式讓這個正規表示式更容易閱讀

\b(?'word'
  
(?'oddword' (?'oddletter' [a-z])(?P>oddword)  \k'oddletter' |[a-z])
| (?'evenword'(?'evenletter'[a-z])(?P>evenword)?\k'evenletter')
)\b

基本上,這是兩個原始正規表示式的拷貝與交替組合。第一個交替選項將群組「word」和「letter」重新命名為「oddword」和「oddletter」。第二個交替選項將群組「word」和「letter」重新命名為「evenword」和「evenletter」。呼叫 (?P>evenword) 現在使用問號,而不是交替選項 |[a-z],變成可選的。新的群組「word」結合了兩個群組「oddword」和「evenword」,以便字元邊界仍適用於整個正規表示式。

此正規表示式中的第一個交替選項「oddword」與主題中討論的正規表示式以完全相同的方式,比對出奇數長度的迴文,例如 radar。遞迴和擷取群組。新正規表示式中的第二個交替選項從未嘗試過。

當字串是偶數長度的迴文時,例如 deed,新正規表示式會先嘗試第一個交替選項的所有排列組合。僅在第一個交替選項找不到比對項時,才會嘗試第二個交替選項「evenword」。

第二個替代方案的開頭與原始正規表示式相同。群組「evenletter」比對 d。在連續三次遞迴中,群組擷取 e、e 和 d。第四次遞迴失敗,因為沒有字元可以比對。回到第三次遞迴,正規表示式引擎會注意到遞迴呼叫 (?P>evenword)? 是選用的。它會繼續進行反向參照 \k'evenletter'。反向參照會失敗,因為字串中沒有任何字元。由於遞迴沒有其他替代方案可以嘗試,因此會回溯。群組「evenletter」必須放棄最近的比對,而 PCRE 會退出失敗的第三次遞迴。

在第二次遞迴中,反向參照會失敗,因為擷取群組在該遞迴中比對 e,但字串中的下一個字元是 d。群組會放棄另一個比對,而 PCRE 會退出失敗的第二次遞迴。

回到第一次遞迴,反向參照會成功。群組在該遞迴中比對字串中的第一個 e,而反向參照會比對第二個。PCRE 會退出成功的第一次遞迴。

回到整體比對嘗試中,反向參照會再次成功。群組在整體比對嘗試中比對字串開頭的 d,而反向參照會比對最後一個 d。退出群組「evenword」和「word」,字詞邊界會比對字串的結尾。 deed 是整體比對。

Recursion and Subroutine Calls May or May Not Be Atomic
  • 简
  • 繁
  • En
About Regular Expressions » Regular Expressions Tutorial » Recursion and Subroutine Calls May or May Not Be Atomic

Regex Tutorial
Introduction
Table of Contents
Special Characters
Non-Printable Characters
Regex Engine Internals
Character Classes
Character Class Subtraction
Character Class Intersection
Shorthand Character Classes
Dot
Anchors
Word Boundaries
Alternation
Optional Items
Repetition
Grouping & Capturing
Backreferences
Backreferences, part 2
Named Groups
Relative Backreferences
Branch Reset Groups
Free-Spacing & Comments
Unicode
Mode Modifiers
Atomic Grouping
Possessive Quantifiers
Lookahead & Lookbehind
Lookaround, part 2
Keep Text out of The Match
Conditionals
Balancing Groups
Recursion
Subroutines
Infinite Recursion
Recursion & Quantifiers
Recursion & Capturing
Recursion & Backreferences
Recursion & Backtracking
POSIX Bracket Expressions
Zero-Length Matches
Continuing Matches
More on This Site
Introduction
Regular Expressions Quick Start
Regular Expressions Tutorial
Replacement Strings Tutorial
Applications and Languages
Regular Expressions Examples
Regular Expressions Reference
Replacement Strings Reference

Recursion and Subroutine Calls May or May Not Be Atomic

Earlier topics in this tutorial explain regular expression recursion and regular expression subroutines. In this topic the word “recursion” refers to recursion of the whole regex, recursion of capturing groups, and subroutine calls to capturing groups.

Perl and Ruby backtrack into recursion if the remainder of the regex after the recursion fails. They try all permutations of the recursion as needed to allow the remainder of the regex to match. PCRE treats recursion as atomic. PCRE backtracks normally during the recursion, but once the recursion has matched, it does not try any further permutations of the recursion, even when the remainder of the regex fails to match. The result is that Perl and Ruby may find regex matches that PCRE cannot find, or that Perl and Ruby may find different regex matches.

Consider the regular expression aa$|a(?R)a|a in Perl or the equivalent aa$|a\g'0'a|a in Ruby 2.0. PCRE supports either syntax. Let’s see how Perl, Ruby, and PCRE go through the matching process of this regex when aaa is the subject string.

The first alternative aa$ fails because the anchor cannot be matched between the second and third a in the string. Attempting the second alternative at the start of the string, a matches a. Now the regex engine enters the first recursion.

Inside the recursion, the first alternative matches the second and third a in the string. The regex engine exits a successful recursion. But now, the a that follows (?R) or \g'0' in the regex fails to match because the regex engine has already reached the end of the string. Thus the regex engine must backtrack. Here is where PCRE behaves differently than Perl or Ruby.

Perl and Ruby remember that inside the recursion the regex matched the second alternative and that there are three possible alternatives. Perl and Ruby backtrack into the recursion. The second alternative inside the recursion is backtracked, reducing the match so far to the first a in the string. Now the third alternative is attempted. a matches the second a in the string. The regex engine again exits successfully from the same recursion. This time, the a that follows (?R) or \g'0' in the regex matches the third a in the string. aaa is found as the overall match.

PCRE, on the other hand, remembers nothing about the recursion other than that it matched aa at the end of the string. PCRE does backtrack over the recursion, reducing the match so far to the first a in the string. But this leaves the second alternative in the regex without any further permutations to try. Thus the a at the start of the second alternative is also backtracked, reducing the match so far to nothing. PCRE continues the match attempt at the start of the string with the third alternative and finds that a matches a at the start of the string. In PCRE, this is the overall match.

You can make recursion in Perl and Ruby atomic by adding an atomic group. aa$|a(?>(?R))a|a in Perl and aa$|a(?>\g'0')a|a in Ruby is the same as the original regexes in PCRE.

Boost is of two minds. Recursion of the whole regex is atomic in Boost, like in PCRE. But Boost will backtrack into subroutine calls and into recursion of capturing groups, like Perl. So you can do non-atomic recursion in Boost by wrapping the whole regex into a capturing group and then calling that.

PCRE2 originally behaved like PCRE, treating all recursion and subroutine calls as atomic. PCRE2 10.30 changed this, trying to be more like Perl, but ending up like Boost. PCRE2 10.30 will backtrack into subroutine calls and recursion of capturing groups like Perl does. But PCRE2 is still not able to backtrack into recursion of the whole regex. In the examples below, “PCRE” means the original PCRE only. For PCRE2 10.22 and prior, follow the PCRE example. For PCRE2 10.30 and later, follow the Perl example.

Palindromes of Any Length in Perl and Ruby

The topic about recursion and capturing groups explains a regular expression to match palindromes that are an odd number of characters long. The solution seems trivial. \b(?'word'(?'letter'[a-z])(?&word)\k'letter'|[a-z]?)\b does the trick in Perl. The quantifier ? makes the [a-z] that matches the letter in the middle of the palindrome optional. In Ruby we can use \b(?'word'(?'letter'[a-z])\g'word'\k'letter+0'|[a-z]?)\b which adds the same quantifier to the solution that specifies the recursion level for the backreference. In PCRE, the Perl solution still matches odd-length palindromes, but not even-length palindromes.

Let’s see how these regexes match or fail to match deed. PCRE starts off the same as Perl and Ruby, just as in the original regex. The group “letter” matches d. During three consecutive recursions, the group captures e, e, and d. The fourth recursion fails, because there are no characters left to match. Back in the third recursion, the first alternative is backtracked and the second alternative matches d at the end of the string. The engine exits the third recursion with a successful match. Back in the second recursion, the backreference fails because there are no characters left in the string.

Here the behavior diverges. Perl and Ruby backtrack into the third recursion and backtrack the quantifier ? that makes the second alternative optional. In the third recursion, the second alternative gives up the d that it matched at the end of the string. The engine exits the third recursion again, this time with a successful zero-length match. Back in the second recursion, the backreference still fails because the group stored e for the second recursion but the next character in the string is d. Thus the first alternative is backtracked and the second alternative matches the second e in the string. The second recursion is exited with success.

In the first recursion, the backreference again fails. The group stored e for the first recursion but the next character in the string is d. Again, Perl and Ruby backtrack into the second recursion to try the permutation where the second alternative finds a zero-length match. Back in the first recursion again, the backreference now matches the second e in the string. The engine leaves the first recursion with success. Back in the overall match attempt, the backreference matches the final d in the string. The word boundary succeeds and an overall match is found.

PCRE, however, does not backtrack into the third recursion. It does backtrack over the third recursion when it backtracks the first alternative in the second recursion. Now, the second alternative in the second recursion matches the second e in the string. The second recursion is exited with success.

In the first recursion, the backreference again fails. The group stored e for the first recursion but the next character in the string is d. Again, PCRE does not backtrack into the second recursion, but immediately fails the first alternative in the first recursion. The second alternative in the first recursion now matches the first e in the string. PCRE exits the first recursion with success. Back in the overall match attempt, the backreference fails, because the group captured d prior to the recursion, and the next character is the second e in the string. Backtracking again, the second alternative in the overall regex match now matches the first d in the string. Then the word boundary fails. PCRE did not find any matches.

Palindromes of Any Length in PCRE

To match palindromes of any length in PCRE, we need a regex that matches words of an even number of characters and of an odd number of characters separately. Free-spacing mode makes this regex easier to read:

\b(?'word'
  
(?'oddword' (?'oddletter' [a-z])(?P>oddword)  \k'oddletter' |[a-z])
| (?'evenword'(?'evenletter'[a-z])(?P>evenword)?\k'evenletter')
)\b

Basically, this is two copies of the original regex combined with alternation. The first alternatives has the groups “word” and “letter” renamed to “oddword” and “oddletter”. The second alternative has the groups “word” and “letter” renamed to “evenword” and “evenletter”. The call (?P>evenword) is now made optional with a question mark instead of the alternative |[a-z]. A new group “word” combines the two groups “oddword” and “evenword” so that the word boundaries still apply to the whole regex.

The first alternative “oddword” in this regex matches a palindrome of odd length like radar in exactly the same way as the regex discussed in the topic about recursion and capturing groups does. The second alternative in the new regex is never attempted.

When the string is a palindrome of even length like deed, the new regex first tries all permutations of the first alternative. The second alternative “evenword” is attempted only after the first alternative fails to find a match.

The second alternative starts off in the same way as the original regex. The group “evenletter” matches d. During three consecutive recursions, the group captures e, e, and d. The fourth recursion fails, because there are no characters left to match. Back in the third recursion, the regex engine notes that recursive call (?P>evenword)? is optional. It proceeds to the backreference \k'evenletter'. The backreference fails because there are no characters left in the string. Since the recursion has no further alternatives to try, it is backtracked. The group “evenletter” must give up its most recent match and PCRE exits from the failed third recursion.

In the second recursion, the backreference fails because the capturing group matched e during that recursion but the next character in the string is d. The group gives up another match and PCRE exits from the failed second recursion.

Back in the first recursion, the backreference succeeds. The group matched the first e in the string during that recursion and the backreference matches the second. PCRE exits from the successful first recursion.

Back in the overall match attempt, the backreference succeeds again. The group matched the d at the start of the string during the overall match attempt, and the backreference matches the final d. Exiting the groups “evenword” and “word”, the word boundary matches at the end of the string. deed is the overall match.

©2015-2025 艾丽卡 support@alaica.com