条件匹配是什么
条件匹配让模式根据「前面某个组有没有参与匹配」决定后面走哪个分支,写法是 (?(条件)匹配则用|否则用)。它解决的是「同一段文本有两种合法形态」的问题:比如引号可有可无,一旦出现开头引号,结尾就必须也有。相比拆成两条模式按顺序尝试,条件把两种形态写在一处,代价是引擎支持面窄、可读性差。
基本信息
| 语法形态 | (?(1)yes|no) 按组号,(?(name)yes|no) 按组名,no 分支可省略 |
|---|---|
| 断言条件 | (?(?=regex)yes|no),仅 PCRE、Perl、.NET 等支持,Python re 不接受 |
| 支持情况 | PCRE/PHP、Perl、.NET 支持完整;Python re 支持按组号与组名;Java 不支持 |
| 判定依据 | 判断的是该组在本次匹配中是否参与了,与组内文本长短无关 |
典型使用场景
- 可选取值定界:开头有引号时才强制结尾也有引号,避免把交叉写法当成合法
- 可选前缀决定长度:出现国家码时号码位数用一种规则,否则用另一种
- 单位后缀影响格式:数字带大小写单位时才要求小数位,否则只允许整数
- 把两三种分支收敛到一条模式里,减少调用方按顺序试探模式的代码量
- PCRE 递归场景中判断当前是否处于递归调用内,用来区分首层与嵌套层
条件匹配写法示例
条件匹配写法示例
(a)?(?(1)b|c) # 组 1 参与就要求 b,未参与则要求 c
(?P<q>['"])?(?P<v>[^'"]*)(?(q)['"]) # 有开头引号才要求结尾引号
(?(?=x)y|z) # 断言条件,仅 PCRE、Perl、.NET 支持
(?(R)z|y) # PCRE:处于递归调用内则匹配 z
(?(DEFINE)(?<byte>0[xX][0-9A-F]+)) # PCRE 定义块,只定义子模式不参与匹配引擎支持对照
| 引擎 | 按组号/组名 | 断言条件 | 递归条件 |
|---|---|---|---|
| PCRE / PHP / Perl | 支持 | 支持 | 支持 (?(R)…) |
| Python re | 支持,且组必须已出现 | 不支持,会编译报错 | 不支持 |
| .NET | 支持 | 支持 | 不支持 |
| Java | 不支持 | 不支持 | 不支持 |
条件里引用的组必须先出现在它前面:Python re 对引用后文组会直接报 invalid group reference,PCRE 也有类似约束,改写模式时把被判断的组整体前移是常见修法。
条件判断的是参与与否而不是内容是否为空:用 * 或 ? 声明的组即使匹配到空字符串也算参与,此时仍走 yes 分支,用「有没有引号」这类需求做判断时要把量词写准确。