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

原子分组

原子组是一种组,当正则表达式引擎退出该组时,会自动丢弃该组内任何标记记住的所有回溯位置。原子组是无捕获的。语法为 (?>)环视组也是原子的。大多数现代正则表达式风格都支持原子分组,包括 JGsoft 风格、JavaPCRE.NETPerlBoostRuby。其中大多数还支持 占有量词,这本质上是原子分组的一种符号便利。 Python 从 Python 3.11 版本开始支持原子分组和占有量词。

一个示例将阐明原子组的行为。正则表达式 a(bc|b)c(捕获组)匹配 abccabc。正则表达式 a(?>bc|b)c(原子组)匹配 abcc 但不匹配 abc

应用于 abc 时,两个正则表达式都将匹配 aabcbc,然后 c 将无法匹配字符串末尾。此处它们的路径不同。带有捕获组的正则表达式已记住交替的回溯位置。该组将放弃其匹配,b 然后匹配 bc 匹配 c。找到匹配项!

然而,带有原子组的正则表达式在匹配 bc 后退出了原子组。此时,该组内标记的所有回溯位置都会被丢弃。在此示例中,交替在字符串第二个位置尝试 b 的选项被丢弃。结果,当 c 失败时,正则表达式引擎没有其他可尝试的替代方案。

当然,上面的示例并不是很有用。但它确实非常清楚地说明了原子分组如何消除某些匹配。或者更重要的是,它消除了某些匹配尝试。

使用原子分组进行正则表达式优化

考虑正则表达式 \b(integer|insert|in)\b 和主题 integers。显然,由于单词边界,它们不匹配。不太明显的是,正则表达式引擎将花费相当多的精力来弄清楚这一点。

\b 在字符串的开头匹配,integer 匹配 integer。正则表达式引擎注意到组中还有两个备选方案,并继续使用 \b。这无法在 rs 之间匹配。因此,引擎回溯以尝试组内的第二个备选方案。第二个备选方案匹配 in,但随后无法匹配 s。因此,引擎再次回溯到第三个备选方案。in 匹配 in\b 这次在 nt 之间失败。正则表达式引擎不再记住回溯位置,因此它声明失败。

这是很多工作,以弄清楚 integers 不在我们的单词列表中。我们可以通过告诉正则表达式引擎,如果它在匹配 integer 后无法匹配 \b,那么它不应该尝试任何其他单词来优化这一点。我们在主题字符串中遇到的单词是一个较长的单词,它不在我们的列表中。

我们可以通过将捕获组转换为原子组来实现这一点:\b(?>integer|insert|in)\b。现在,当 integer 匹配时,引擎将从原子组退出,并丢弃为交替存储的回溯位置。当 \b 失败时,引擎会立即放弃。在扫描大型文件以查找长关键字列表时,这种节省可能是显着的。当您的备选方案包含重复标记(更不用说重复组)导致灾难性回溯时,这种节省将至关重要。

不要过快地将所有组设为原子组。正如我们在上面的第一个示例中看到的那样,原子分组也会排除有效的匹配。比较当应用于 insert\b(?>integer|insert|in)\b\b(?>in|integer|insert)\b 的行为。前一个正则表达式匹配,而后一个失败。如果组不是原子组,则两个正则表达式都会匹配。请记住,交替从左到右尝试其备选方案。如果第二个正则表达式匹配 in,则由于原子组,它不会尝试其他两个备选方案。