平衡组匹配配对括号
平衡组是 .NET 正则独有的机制:命名组可以被多次捕获并形成栈,用 (?<组名>…) 入栈、用 (?<-组名>…) 出栈,再用条件判断栈是否为空,从而校验括号、标签是否配平。它的本质是把 .NET 的分组捕获集合当成计数器使用,因此只有 .NET 能用这套写法,PCRE 系一般改用递归子模式。
基本信息
| 适用范围 | .NET(Regex 类)独有,PCRE、Java、Python re、JS 均不支持 |
|---|---|
| 入栈写法 | (?<组名>…) 捕获内容并入栈一层 |
| 出栈写法 | (?<-组名>…) 从栈顶弹出一层,栈已空则整个分支匹配失败 |
| 配平校验 | (?(组名)(?!)) 断言栈为空,未加这一句就只能保证局部配对 |
典型使用场景
- 校验表达式括号配平:把左括号入栈、右括号出栈,最后要求栈空
- 提取最外层括号内容:在配平的前提下取整段,而不是靠贪婪量词猜边界
- 检查模板分隔符成对:{{ 与 }} 混在文本里时确认没有落单的分隔符
- 校验标签配对:要求 <b>…</b> 这类同名开闭标签成对出现,用于规整片段
- 统计嵌套层数:通过 CaptureCollection 取出各层捕获,做深度分析
平衡组写法示例
平衡组写法示例
(?<DEPTH>\() # 遇到左括号入栈一层
(?<-DEPTH>\)) # 遇到右括号出栈,栈空时该分支匹配失败
(?(DEPTH)(?!)) # 结束后断言栈为空,否则括号未配平
^(?:[^()]|(?<D>\()|(?<-D>\)))*(?(D)(?!))$ # 完整配平校验模式
Regex.Match(s, pat).Groups["D"].Captures.Count # 取出各层捕获,判断嵌套深度配平需求与实现手段
| 需求 | .NET 写法 | 其他引擎的替代 |
|---|---|---|
| 括号配平校验 | 平衡组加 (?(组名)(?!)) | PCRE/Perl 用递归子模式 |
| 取最外层内容 | 配平后取整段捕获 | 递归子模式或解析器 |
| 标签成对 | 平衡组配合命名引用 | 递归子模式,或改用解析器 |
| 层数统计 | CaptureCollection 逐层读取 | 代码侧用计数器扫描 |
平衡组只做数量配平,不检查语义:((a) 会被判为不配平,但 [a) 这类交叉写法在纯计数场景下也可能被放过,需要按业务再加断言或分组约束。
如果不做复杂提取,用代码扫描字符并维护计数器往往更直观、更容易调试;正则在配平场景的优势在于能与其他字段提取写在一条模式里一次性完成。