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

时间:2013-01-19 06:31:59

标签: ruby regex ruby-1.9.3

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

  1. 为什么名称来自 “原子分组” ?什么 “原子性” 它有 一般分组 没有。
  2. 原子分组 常规分组 的不同之处?
  3. 为什么 原子组 被称为 非捕获 组?
  4. 我尝试了下面的代码来理解,但是对输出有疑问,以及它们在同一个字符串上的工作方式有多么不同?

    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 个答案:

答案 0 :(得分:39)

()有一些属性(包括(?!pattern)(?=pattern)等属性和普通(pattern)),但所有属性之间的共同属性是分组,它使任意模式成为一个单元(单位是我自己的术语),这在重复中很有用。

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

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

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

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

示例

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

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


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

此正则表达式有3个匹配项,分别为bbabbbabbbbc。如果您使用第二个正则表达式,它是相同的但添加了捕获组以进行调试,您可以看到所有匹配都是匹配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
    
  • 通过原子分组,.*的所有可能性都被切断并限制在第一场比赛中。因此,在贪吃整个字符串并且无法匹配之后,引擎必须转到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
    

    随后的比赛将从此处继续。

答案 1 :(得分:4)

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

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

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

答案 2 :(得分:3)

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

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

以粗体显示

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

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

对于第二行,我们的正则表达式会找到the。 它将继续并查看big,跳过它,查看并找到small。 然后找到&#34; &#34 ;. 它看着&#34; cat&#34 ;,跳过它,看着&#34; dog&#34;,跳过它,然后找到&#34; bird&#34;。

对于第三行,我们的正则表达式会找到the, 它继续并找到与{em>即时要求匹配的big,然后继续。 它无法找到空间,因此它回溯(将位置倒回到它所做的最后一个选择)。 它会跳过big,查看small并跳过它。它找到最大的也匹配立即要求。 然后找到&#34; &#34 ;. 它会查看cat并跳过它,并匹配dog

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

现在考虑the (?>big|small|biggest) (cat|dog|bird) 注意形容词上的?>原子组。

以粗体显示

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

对于第一行,第二行和第四行,我们的引擎以相同的方式运行。

对于第三行,我们的正则表达式会找到the, 它继续发现&#34; big&#34;它匹配立即要求,然后继续。 它无法找到空间,但作为引擎制作的最后选择的原子组不会允许重新检查 选项(禁止回溯)。 由于它无法做出新的选择,因此匹配必须失败,因为我们的简单表达式没有其他选择。

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

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

还有很多选择,引擎无法通过可能看起来不匹配的东西来决定。如果您有((red)?cat|dog|bird),则引擎会查看&#34; r&#34;,退出,注意?量词,忽略子组(red),然后查找匹配项。