快速入门
教程
工具和语言
示例
参考
书评
正则表达式教程
简介
目录
特殊字符
不可打印字符
正则表达式引擎内部
字符类
字符类减法
字符类交集
速记字符类
锚点
单词边界
交替
可选项
重复
分组和捕获
反向引用
反向引用,第 2 部分
命名组
相对反向引用
分支重置组
自由间距和注释
Unicode
模式修饰符
原子分组
所有格量词
前瞻和后顾
环视,第 2 部分
将文本保留在匹配之外
条件
平衡组
递归
子例程
无限递归
递归和量词
递归和捕获
递归和反向引用
递归和回溯
POSIX 方括号表达式
零长度匹配
继续匹配
本网站上的更多内容
简介
正则表达式快速入门
正则表达式教程
替换字符串教程
应用程序和语言
正则表达式示例
正则表达式参考
替换字符串参考
书评
可打印 PDF
关于本网站
RSS 提要和博客
RegexBuddy—Better than a regular expression tutorial!

无限递归

诸如 (?R)?za?(?R)?za|(?R)z 这样的正则表达式在递归前没有任何必须匹配的内容,会导致无限递归。如果正则表达式引擎在文本中未前进便达到递归,则下一次递归在文本中未前进便会再次达到递归。对于第一个正则表达式,这种情况在匹配尝试开始时立即发生。对于其他两个正则表达式,这种情况在没有更多字母 a 需要匹配时发生。

JGsoft V2 和 Boost 1.64 将前两个正则表达式视为语法错误,因为它们始终导致无限递归。它们允许第三个正则表达式,因为该正则表达式可以匹配 a。Ruby 1.9 及更高版本、所有版本的 PCRE 以及 PCRE2 10.20 及更低版本将所有三种形式的潜在无限递归视为语法错误。Perl、PCRE2 10.21 及更高版本以及 Boost 1.63 及更低版本允许所有三种形式。

循环无限子例程调用

子例程调用也可能导致无限递归。所有风格在 ((?1)?z)(a?(?1)?z)(a|(?1)z) 中处理潜在的无限递归,方式与处理整个正则表达式的潜在无限递归相同。

但是,如果它们调用的组有另一个子例程调用,该调用调用第一个子例程调用的父组,那么本身不是递归的子例程调用最终可能会变成递归。当子例程调用被迫循环时,也会导致无限递归。在编译正则表达式时检测到此类循环调用比检查直接无限递归要复杂。只有 JGsoft V2 和 Ruby 1.9 及更高版本能够检测到这种情况并将其视为语法错误。所有其他风格都允许这些正则表达式。

错误和崩溃

当无限递归确实发生时,无论是直接递归还是子例程调用循环,JGsoft V2、Perl 和 PCRE2 都会将其视为匹配错误,从而中止整个匹配尝试。Boost 1.64 通过不尝试递归并表现得好像递归失败来处理这种情况。如果递归是可选的,那么 Boost 1.64 可能会找到其他风格抛出错误的匹配项。

当发生无限递归时,Boost 1.63 及更早版本和 PCRE 8.12 及更早版本会崩溃。这也影响了 Delphi(最高版本为 XE6)和 PHP(最高版本为 5.4.8),因为它们基于较旧的 PCRE 版本。

无限递归

具有非可选递归标记且没有不带相同递归的备选方案的正则表达式(例如 a(?R)z)会导致无限递归。此类正则表达式永远找不到匹配项。当 a 匹配时,正则表达式引擎会尝试递归。如果它可以匹配另一个 a,那么它必须再次尝试递归。最终,a 将用完要匹配的字母。然后递归失败。由于它不是可选的,因此正则表达式匹配失败。

JGsoft V2 和 Ruby 在编译正则表达式时会检测到这种情况。它们将无限递归标记为语法错误。Perl、PCRE、PCRE2 和 Boost 不会检测到无限递归。它们只是经历匹配过程,该过程找不到匹配项。