我已经看过了Atomic Grouping和rubyinfo的文档,我想到了一些问题:
1.为什么叫***“原子分组”***?它有什么“原子性”是一般分组没有的?
1.***原子分组***与***常规分组***有何不同?
1.为什么***原子组***被称为***非捕获***组?
我尝试了下面的代码来理解,但对输出以及它们在同一字符串上的工作方式有何不同感到困惑?
irb(main):001:0> /a(?>bc|b)c/ =~ "abbcdabcc"
=> 5
irb(main):004:0> $~
=> #<MatchData "abcc">
irb(main):005:0> /a(bc|b)c/ =~ "abcdabcc"
=> 0
irb(main):006:0> $~
=> #<MatchData "abc" 1:"b">
3条答案
按热度按时间z4iuyo4d1#
()
具有一些属性(包括(?!pattern)
、(?=pattern)
等)。和普通的(pattern)
),但它们之间的共同属性是 grouping,这使得任意模式成为一个单元(unit是我自己的术语),这在重复中很有用。普通捕获
(pattern)
具有 capturing 和 group 属性。捕获意味着匹配模式里面的文本将被捕获,这样你就可以用它来进行反向引用、匹配或替换。非捕获组(?:pattern)
没有捕获属性,因此与(pattern)
相比,它将保存一些空间并提高一些速度,因为它不存储与内部模式匹配的字符串的开始和结束索引。原子分组
(?>pattern)
也具有非捕获属性,因此内部匹配的文本的位置将不会被捕获。与捕获组或非捕获组相比,原子分组增加了 atomic 属性。atomic是指:在当前位置,找到与原子分组内的模式匹配的first序列(first由引擎如何根据给定的模式进行匹配来定义),并保留它(因此不允许回溯)。
没有原子性的组将允许回溯-它仍然会找到第一个序列,然后如果前面的匹配失败,它将回溯并找到下一个序列,直到找到整个正则表达式的匹配或所有可能性都被耗尽。
示例
输入字符串:
bbabbbabbbbc
图案:
/(?>.*)c/
.*
的第一个匹配是bbabbbabbbbc
,因为贪婪量词*
。它将保留此匹配,不允许c
进行匹配。匹配器将在字符串末尾的下一个位置重试,并且发生相同的事情。所以根本没有匹配正则表达式的东西。输入字符串:
bbabbbabbbbc
图案:
/((?>.*)|b*)[ac]/
,用于测试/(((?>.*))|(b*))[ac]/
这个正则表达式有3个匹配项,分别是
bba
,bbba
,bbbbc
。如果你使用第二个正则表达式,这是相同的,但添加了用于调试目的的捕获组,你可以看到所有的匹配都是内部匹配b*
的结果。你可以在这里看到回溯行为。
/(.*|b*)[ac]/
,字符串将有一个单一的匹配,这是整个字符串,由于在最后回溯匹配[ac]
。请注意,引擎将返回到.*
以回溯1个字符,因为它仍然有其他可能性。.*
的所有可能性都被切断并限制在第一个匹配中。因此,在贪婪地吃掉整个字符串并且无法匹配之后,引擎必须去b*
模式,在那里它成功地找到了与正则表达式的匹配。接下来的比赛将从这里继续进行。
bejyjqdl2#
我最近不得不向其他人解释原子组,我想我会调整并在这里分享这个例子。
考虑
/the (big|small|biggest) (cat|dog|bird)/
粗体匹配
*大狗
*小鸟
*最大的狗
*小猫
DEMO
对于第一行,正则表达式引擎会找到
the
。然后它会继续我们的形容词(big
,small
,biggest
),它找到big
。匹配big
后,继续查找空间。然后,它会查看我们的宠物(cat
,dog
,bird
),找到cat
,跳过它,找到dog
。对于第二行,我们的正则表达式将找到
the
。它将继续并查看big
,跳过它,查看并找到small
。它找到空格,跳过cat
和dog
,因为它们不匹配,然后找到bird
。对于第三行,我们的正则表达式将找到
the
,它继续并找到匹配 immediate requirement 的big
,并继续。它找不到空格,所以它 backtracks(将位置倒回它所做的最后一个选择)。它跳过big
,跳过small
,并找到biggest
,它也匹配 * 立即要求 *。然后它找到了空间。它跳过cat
,匹配dog
。对于第四行,我们的正则表达式将找到
the
。它将继续查看big
,跳过它,查看并找到small
。然后它找到了空间。它查看并匹配cat
。考虑
/the (?>big|small|biggest) (cat|dog|bird)/
注意形容词上的
?>
原子团。粗体匹配
*大狗
*小鸟
*小猫
DEMO
对于第一行、第二行和第四行,我们将得到相同的结果。
对于第三行,我们的正则表达式将找到
the
,它继续并找到匹配 immediate requirement 的big
,然后继续。它找不到空格,但原子组是引擎做出的最后选择,不允许重新检查 * 那个 * 选择(禁止回溯)。由于它不能做出新的选择,匹配必须失败,因为我们的简单表达式没有其他选择。cat
来知道它不匹配dog
,只需查看c
就足够了。当试图匹配bird时,cat
中的c
和dog中的d
足以告诉引擎检查其他选项。但是如果你有...
((cat|snake)|dog|bird)
,引擎当然也需要在snake掉到前一组并检查dog和bird之前检查它。((red)?cat|dog|bird)
,引擎将查看r
,退出,注意?
量词,忽略子组(red)
,并查找匹配。sigwle7e3#
“原子组”是正则表达式永远不会回溯过去的组。因此,在第一个例子
/a(?>bc|b)c/
中,如果组中的bc
交替匹配,那么它将永远不会返回并尝试b
交替。如果稍微修改第一个示例,使其与"abcdabcc"
匹配,那么您将看到它仍然匹配字符串末尾的"abcc"
,而不是字符串开头的"abc"
。如果不使用原子组,则它可以回溯bc
并尝试b
交替,并最终在开始时匹配"abc"
。至于第二个问题,它有什么不同,这只是你第一个问题的重述。
最后,原子组不被“称为”非捕获组。这不是他们的别名非捕获组是不捕获其内容的组。通常,当您将正则表达式与字符串进行匹配时,您可以检索所有匹配的组,如果您使用替换,则可以在替换中使用反向引用(如
\1
)将捕获的组插入其中。但非捕获组不提供这一点。经典的非捕获组是(?:pattern)
。一个原子组碰巧也具有非捕获属性,因此它被称为非捕获组。