()
拥有一些属性(包括 (?!pattern)
, (?=pattern)
, 等等,还有普通的 (pattern)
),但是它们所有的共同特征都是 分组,这使得任意的模式成为了一个单元(单元是我自己的术语),在重复中非常有用。
普通捕获 (pattern)
具有 捕获 和 分组 的属性。捕获意味着文本匹配内部的模式将被捕获,以便您可以在匹配或替换中使用反向引用。非捕获组 (?:pattern)
没有捕获属性,因此与 (pattern)
相比,它会节省一些空间并加速匹配,因为它不存储与模式匹配的字符串的起始和结束索引。
原子组 (?>pattern)
也具有非捕获属性,因此不会捕获文本匹配的位置。
与捕获或非捕获组相比,原子组添加了 原子 特性。在这里,原子意味着:在当前位置,查找匹配模式内的第一个序列(由引擎根据给定模式的匹配方式定义),并保持它(因此不允许回溯)。
没有原子性的组将允许回溯-它仍然会找到第一个序列,然后如果匹配失败,则会回溯并查找下一个序列,直到找到整个正则表达式的匹配或所有可能性都耗尽为止。
示例
输入字符串:bbabbbabbbbc
模式:/(?>.*)c/
.*
的第一次匹配是由于贪婪量词 *
而得到的,为 bbabbbabbbbc
。它将保留此匹配,禁止匹配 c
。匹配器将在下一个位置重试到字符串的末尾,并发生相同的事情。因此,根本没有匹配正则表达式。
输入字符串:bbabbbabbbbc
模式:/((?>.*)|b*)[ac]/
,用于测试 /(((?>.*))|(b*))[ac]/
这个正则表达式有三个匹配项,分别是 bba
,bbba
,bbbbc
。如果使用带有捕获组的第二个正则表达式进行调试,可以看到所有的匹配都是由于匹配内部的 b*
而得到的。
您可以在这里看到回溯行为。
Pattern: /(.*|b*)[ac]/
bbabbbabbbbc
^ -- Start matching. Look at first item in alternation: .*
bbabbbabbbbc
^ -- First match of .*, due to greedy quantifier
bbabbbabbbbc
X -- [ac] cannot match
-- Backtrack to ()
bbabbbabbbbc
^ -- Continue explore other possibility with .*
-- Step back 1 character
bbabbbabbbbc
^ -- [ac] matches, end of regex, a match is found
使用原子分组,所有可能性的 .*
都被切断并限制在第一个匹配上。所以在贪婪地匹配整个字符串后失败,引擎必须去匹配 b*
模式,这里成功地找到了正则表达式的一个匹配。
Pattern: /((?>.*)|b*)[ac]/
bbabbbabbbbc
^ -- Start matching. Look at first item in alternation: (?>.*)
bbabbbabbbbc
^ -- First match of .*, due to greedy quantifier
-- The atomic grouping will disallow .* to be backtracked and rematched
bbabbbabbbbc
X -- [ac] cannot match
-- Backtrack to ()
-- (?>.*) is atomic, check the next possibility by alternation: b*
bbabbbabbbbc
^ -- Starting to rematch with b*
bbabbbabbbbc
^ -- First match with b*, due to greedy quantifier
bbabbbabbbbc
^ -- [ac] matches, end of regex, a match is found
接下来的比赛将从这里继续进行。
grouping
时,我们为什么需要atomic grouping
,它能做什么一般的grouping
不能做到的事情。你能帮助我澄清这些基本概念吗? - Arup Rakshit