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

laawzig2  于 2023-05-17  发布在  Ruby
关注(0)|答案(3)|浏览(111)

我已经看过了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">
z4iuyo4d

z4iuyo4d1#

()具有一些属性(包括(?!pattern)(?=pattern)等)。和普通的(pattern)),但它们之间的共同属性是 grouping,这使得任意模式成为一个单元(unit是我自己的术语),这在重复中很有用。
普通捕获(pattern)具有 capturinggroup 属性。捕获意味着匹配模式里面的文本将被捕获,这样你就可以用它来进行反向引用、匹配或替换。非捕获组(?:pattern)没有捕获属性,因此与(pattern)相比,它将保存一些空间并提高一些速度,因为它不存储与内部模式匹配的字符串的开始和结束索引。
原子分组(?>pattern)也具有非捕获属性,因此内部匹配的文本的位置将不会被捕获。
与捕获组或非捕获组相比,原子分组增加了 atomic 属性。atomic是指:在当前位置,找到与原子分组内的模式匹配的first序列(first由引擎如何根据给定的模式进行匹配来定义),并保留它(因此不允许回溯)。
没有原子性的组将允许回溯-它仍然会找到第一个序列,然后如果前面的匹配失败,它将回溯并找到下一个序列,直到找到整个正则表达式的匹配或所有可能性都被耗尽。

示例

输入字符串: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

接下来的比赛将从这里继续进行。

bejyjqdl

bejyjqdl2#

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

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

粗体匹配

*大狗
*小鸟
*最大的狗
*小猫

DEMO
对于第一行,正则表达式引擎会找到the。然后它会继续我们的形容词(bigsmallbiggest),它找到big。匹配big后,继续查找空间。然后,它会查看我们的宠物(catdogbird),找到cat,跳过它,找到dog
对于第二行,我们的正则表达式将找到the。它将继续并查看big,跳过它,查看并找到small。它找到空格,跳过catdog,因为它们不匹配,然后找到bird
对于第三行,我们的正则表达式将找到the,它继续并找到匹配 immediate requirementbig,并继续。它找不到空格,所以它 backtracks(将位置倒回它所做的最后一个选择)。它跳过big,跳过small,并找到biggest,它也匹配 * 立即要求 *。然后它找到了空间。它跳过cat,匹配dog
对于第四行,我们的正则表达式将找到the。它将继续查看big,跳过它,查看并找到small。然后它找到了空间。它查看并匹配cat

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

注意形容词上的?>原子团。
粗体匹配

*大狗
*小鸟

  • 最大的狗
    *小猫

DEMO
对于第一行、第二行和第四行,我们将得到相同的结果。
对于第三行,我们的正则表达式将找到the,它继续并找到匹配 immediate requirementbig,然后继续。它找不到空格,但原子组是引擎做出的最后选择,不允许重新检查 * 那个 * 选择(禁止回溯)。由于它不能做出新的选择,匹配必须失败,因为我们的简单表达式没有其他选择。

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

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

  • 还有很多选择,引擎无法决定,而不去看起来不像匹配的东西,这就是回溯的结果。如果你有((red)?cat|dog|bird),引擎将查看r,退出,注意?量词,忽略子组(red),并查找匹配。
sigwle7e

sigwle7e3#

“原子组”是正则表达式永远不会回溯过去的组。因此,在第一个例子/a(?>bc|b)c/中,如果组中的bc交替匹配,那么它将永远不会返回并尝试b交替。如果稍微修改第一个示例,使其与"abcdabcc"匹配,那么您将看到它仍然匹配字符串末尾的"abcc",而不是字符串开头的"abc"。如果不使用原子组,则它可以回溯bc并尝试b交替,并最终在开始时匹配"abc"
至于第二个问题,它有什么不同,这只是你第一个问题的重述。
最后,原子组不被“称为”非捕获组。这不是他们的别名非捕获组是不捕获其内容的组。通常,当您将正则表达式与字符串进行匹配时,您可以检索所有匹配的组,如果您使用替换,则可以在替换中使用反向引用(如\1)将捕获的组插入其中。但非捕获组不提供这一点。经典的非捕获组是(?:pattern)。一个原子组碰巧也具有非捕获属性,因此它被称为非捕获组。

相关问题