与原子分组混淆 - 它与Ruby正则表达式中的分组有何不同?

Aru*_*hit 18 ruby regex ruby-1.9.3

我刚刚浏览了Atomic Groupingrubyinfo的文档,我想到了一些简单的问题,如下所示:

  1. 为什么这个名字是"Atomic grouping"?什么"原子"它有一般的分组没有.
  2. 如何原子团从不同一般的分组
  3. 为什么原子组被称为非捕获组?

我尝试了下面的代码来理解,但是对输出的混淆以及它们在同一个字符串上的工作方式有多么不同?

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">
Run Code Online (Sandbox Code Playgroud)

nha*_*tdh 42

()有一些性质(包括那些如(?!pattern),(?=pattern)等,以及普通的(pattern)),但所有这些之间的共同属性分组,这使得任意的图案的单个单元(单位是我自己的术语),这是在重复有用.

正常捕获(pattern)具有捕获分组的属性.捕获意味着文本与内部模式匹配将被捕获,以便您可以在匹配或替换时使用反向引用.非捕获组(?:pattern)没有捕获属性,因此它将节省一些空间并加快一点,(pattern)因为它不存储匹配模式内部的字符串的开始和结束索引.

原子分组(?>pattern)还具有非捕获属性,因此不会捕获内部匹配的文本的位置.

与捕获或非捕获组相比,原子分组增加了原子属性.这里的原子意味着:在当前位置,找到第一个序列(第一个是由引擎如何根据给定的模式匹配),它与原子分组中的模式匹配并保持原样(因此不允许回溯).

没有原子性的组将允许回溯 - 它仍将找到第一个序列,然后如果匹配前进失败,它将回溯并找到下一个序列,直到找到整个正则表达式的匹配或所有可能性都用尽.

输入字符串:bbabbbabbbbc
模式:/(?>.*)c/

第一场比赛.*bbabbbabbbbc由于贪婪的量词*.它将保持这场比赛,不允许c匹配.匹配器将在下一个位置重试到字符串的末尾,同样的事情发生.所以没有什么能与正则表达式相提并论.


输入字符串:bbabbbabbbbc
Pattern : /((?>.*)|b*)[ac]/,用于测试/(((?>.*))|(b*))[ac]/

有3场比赛,这正则表达式,它们是bba,bbba,bbbbc.如果您使用第二个正则表达式,它是相同的但添加了捕获组以进行调试,您可以看到所有匹配都是b*内部匹配的结果.

您可以在此处查看回溯行为.

  • 如果没有原子分组/(.*|b*)[ac]/,字符串将具有单个匹配,这是整个字符串,因为最后匹配的回溯[ac].请注意,引擎将.*返回1个字符后退,因为它还有其他可能性.

    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
    
    Run Code Online (Sandbox Code Playgroud)
  • 通过原子分组,所有可能性都.*被切断并限制在第一场比赛中.因此,在贪吃整个字符串并且无法匹配之后,引擎必须转到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
    
    Run Code Online (Sandbox Code Playgroud)

    随后的比赛将从此处继续进行.


Reg*_*Joe 15

我最近不得不向其他人解释原子组,我想我会在这里调整和分享这个例子。

考虑 /the (big|small|biggest) (cat|dog|bird)/

匹配以粗体显示

  • 大狗
  • 小鸟
  • 最大的狗
  • 小猫

演示

对于第一行,正则表达式引擎会找到the . 然后,它会继续在我们的形容词(bigsmallbiggest),它找到big。匹配后big,它继续并找到空间。然后它查看我们的宠物 ( cat, dog, bird),找到cat,跳过它,然后找到dog

对于第二行,我们的正则表达式会找到the . 它将继续查看big,跳过它,查看并查找small。它找到空格,跳过catdog因为它们不匹配,然后找到bird.

对于第三行,我们的正则表达式会找到the ,它会继续查找big直接要求相匹配的内容,然后继续。它找不到空间,所以它回溯(将位置倒回到它所做的最后一个选择)。它跳过big、跳过small和查找biggest也符合直接要求的内容。然后它找到空间。它跳过cat并匹配dog

对于第四行,我们的正则表达式会找到the . 它将继续查看big,跳过它,查看并查找small。然后它找到空间。它查看并匹配cat.


考虑 /the (?>big|small|biggest) (cat|dog|bird)/

注意?>形容词上的原子团。

匹配以粗体显示

  • 大狗
  • 小鸟
  • 最大的狗
  • 小猫

演示

对于第一行、第二行和第四行,我们将得到相同的结果。

对于第三行,我们的正则表达式会找到the ,它继续查找big直接要求匹配的内容,然后继续。它找不到空间,但是作为引擎最后选择的原子组不允许重新检查选择(禁止回溯)。由于它不能做出新的选择,匹配必须失败,因为我们的简单表达式没有其他选择。


这只是一个基本的总结。引擎不需要查看整体cat就知道它不匹配dog,只需查看c就足够了。在尝试匹配 Bird 时,cincatdin dog 足以告诉引擎检查其他选项。

但是,如果您有 ... ((cat|snake)|dog|bird),那么引擎当然也需要在它下降到前一组并检查狗和鸟之前检查蛇。

还有很多选择是引擎无法决定的,除非经过看起来不匹配的事情,这就是导致回溯的原因。如果有((red)?cat|dog|bird),引擎将查看r、退出、注意?量词、忽略子组(red)并寻找匹配项。

  • 这个例子让我很容易最终掌握了这个概念。谢谢! (3认同)

Lil*_*ard 7

“原子组”是正则表达式永远不会回溯过去的一个组。因此,在您的第一个示例中/a(?>bc|b)c/,如果bc组中的交替匹配,那么它永远不会回溯并尝试交替b。如果您稍微更改第一个示例以进行匹配,"abcdabcc"那么您会看到它仍然匹配"abcc"字符串末尾的 ,而不是"abc"开头的 。如果您不使用原子组,那么它可以回溯过去bc并尝试b交替并最终"abc"在开始时匹配 。

至于第二个问题,它有何不同,这只是对第一个问题的改写。

最后,原子组不“称为”非捕获组。这不是他们的替代名称。非捕获组是不捕获其内容的组。通常,当您将正则表达式与字符串匹配时,您可以检索所有匹配的组,如果您使用替换,则可以在替换中使用反向引用,例如\1在其中插入捕获的组。但非捕获组不提供此功能。经典的非捕获群是(?:pattern)。原子团恰好也具有非捕获性质,因此被称为非捕获团。

  • 唔。对我来说,说引擎不会回溯“过去”原子组似乎是错误的。这听起来像是 `/(aab|a)(?&gt;a)ba/` 不会匹配 `aaba`,这是错误的;事实上,引擎会愉快地回溯“过去”原子组,尝试将第一组的匹配从“aab”更改为“a”。引擎在回溯时不会尝试对原子组进行不同的匹配,但*会*回溯*过去*它。 (2认同)