原子组的语义
原子组用圆括号内以问号大于号开头的构造表示。它的行为是:内部子模式一旦匹配成功,就把内部产生的所有选择点丢弃,之后即便整体匹配失败也不再回头改变内部内容。通俗说,它是给分组加了一道「到此为止」的闸门。
两种用途
一是性能:避免内部子模式反复回退,抑制回退爆炸;二是语义:表达「这段一旦确定就不再改」的业务含义。两种用途的方向一致,但都可能改变模式接受的结果集合,用之前要先想清楚是否真的不需要回退。
基本信息
| 写法 | 圆括号内以问号大于号开头,内部是完整子模式 |
|---|---|
| 是否消耗字符 | 会消耗,与断言不同 |
| 是否捕获 | 不捕获内容 |
| 是否回退 | 内部不再回退,外部失败即整体失败 |
| 引擎支持 | PCRE、Java、.NET、Python 3.11 起支持;JavaScript 不支持 |
| 相关写法 | 占有量词是它在量词上的等价物 |
典型使用场景
- 固定一段已经确定的内容,避免后续模式回头改写它的长度
- 消除分支重叠造成的回退爆炸
- 表达「先匹配到这个分隔符,就不再改变」的业务语义
- 复杂模式中把风险段落隔离,限制回退范围
- 替代不支持的占有量词写法,达到相近效果
原子组的行为与边界
原子组的行为与边界
(?>a|ab)c 原子组:先吃 a 就固定,不再试 ab,因此不匹配 abc
(a|ab)c 普通分组:会退回尝试 ab,能匹配 abc
\d+(?>\.\d+)? 常见组合:整数部分确定后不再为小数部分让步
(?>[^,]*), 取到逗号之前的内容后固定,不再回退
[^,]*+, 占有量词写法,与上一行语义相近
# 判断是否该用原子组:问一句「这里允许回头吗」
# 允许回头 -> 普通分组
# 不回头 -> 原子组构造对照
| 构造 | 是否消耗字符 | 是否可回退 | 是否捕获 |
|---|---|---|---|
| 普通分组 | 是 | 是 | 是 |
| 非捕获分组 | 是 | 是 | 否 |
| 原子组 | 是 | 否 | 否 |
| 先行与后行断言 | 否 | 不适用 | 视写法而定 |
| 占有量词 | 是 | 否 | 不适用 |
原子组会改变匹配结果,不是纯粹的性能开关。等价性必须用样本验证:同一批正例与反例在加原子组前后结果必须一致,否则说明某处原本依赖回退。
JavaScript 不支持原子组与占有量词,跨语言复用模式时需预先确认。替代做法是把内部子模式的字符范围收窄,从源头消除多余选择点。