递归子模式是什么
递归子模式让一条模式在匹配过程中回头调用自己(或调用其中某个组),从而处理层数不定的嵌套结构,典型用途是配对括号、嵌套表达式、模板嵌套。它的能力来自回溯式引擎的「子程序调用」机制,因此只在 PCRE、Perl、Oniguruma 以及第三方库里有,标准库里的 Python re、Java、.NET 都不支持。
基本信息
| 整体递归 | (?R) 或 (?0) 表示重新调用整条模式 |
|---|---|
| 按组递归 | (?1)、(?2) 按组号调用子模式,(?&name) 按组名调用,后者可读性更好 |
| 递归条件 | PCRE 的 (?(R)…) 用于判断当前是否位于递归调用内 |
| 支持引擎 | PCRE/PHP、Perl、Oniguruma/Ruby、Python regex 库支持;re、Java、.NET 不支持 |
典型使用场景
- 配对括号:匹配任意层数的圆括号、方括号内容,层数不固定又必须整体配平
- 嵌套表达式片段:从配置或代码里整段抽取形如 f(g(x), y) 的调用结构
- 可嵌套模板语法:处理 {{a{ b }c}} 这类嵌套占位符的完整性校验
- 去掉注释块:把 /* … */ 中再嵌注释的片段作为整体识别出来
- 文本结构合法性预筛:在解析器之前用递归模式快速判断形态,避免更重的解析开销
递归模式写法示例
递归模式写法示例
(?<paren>\((?:[^()]++|(?&paren))*\)) # 匹配任意层括号,占有量词抑制重复回溯
\( (?: (?&paren) | [^()] )* \) # 展开写法:先试递归,再吃普通字符
(?R) # 整条模式递归,把所有规则包成一个整体
\g<0> # Ruby、Oniguruma 里调用整条模式
(?<tpl>\{\{(?:[^{}]++|(?&tpl))*\}\}) # 嵌套占位符,仅适用于内部无其他花括号的文本引擎支持对照
| 引擎 | 递归写法 | 支持程度 |
|---|---|---|
| PCRE / PHP | (?R)、(?1)、(?&name) | 完整支持,含递归条件 |
| Perl | (?R)、(?1)、(?&name) | 完整支持 |
| Ruby / Oniguruma | \g<0>、\g<name> | 支持 |
| Python regex 库 | (?R)、(?&name) | 支持 |
| Python re / Java / .NET | 无 | 不支持,需改用解析器或平衡组 |
递归模式必须保证每轮至少消耗一个字符,否则会陷入无限递归;写法上通常把「普通字符」分支放在递归分支之后,并用占有量词或原子组减少无意义回溯。
递归深度与回溯总量都有引擎上限(PCRE 里是 match limit 与 depth limit),超深输入会匹配失败甚至触发进程保护,线上使用前应对输入长度做硬限制,并准备降级路径。