[英]Confusion with Atomic Grouping - how it differs from the Grouping in regular expression of Ruby?
我瀏覽了Atomic Grouping和rubyinfo的文檔,我想到了一些問題:
我嘗試了下面的代碼來理解但對輸出以及它們在同一字符串上的工作方式有什么不同感到困惑?
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">
A ()
有一些屬性(包括(?!pattern)
、 (?=pattern)
等和普通的(pattern)
),但它們之間的共同屬性是grouping ,這使得任意模式成為single unit(unit是我自己的術語),在重復中很有用。
正常的捕獲(pattern)
具有capture和group的屬性。 捕獲意味着將捕獲與內部模式匹配的文本,以便您可以將其與反向引用、匹配或替換一起使用。 非捕獲組(?:pattern)
沒有捕獲屬性,因此與(pattern)
相比,它會節省一點空間並加快一點速度,因為它不存儲字符串的開始和結束索引匹配里面的模式。
原子分組(?>pattern)
也具有非捕獲屬性,因此不會捕獲內部匹配的文本的位置。
與捕獲或非捕獲組相比,原子分組增加了原子的屬性。 這里的原子意味着:在當前位置,找到與原子分組內的模式匹配的第一個序列(首先由引擎如何根據給定的模式匹配定義)並保持它(因此不允許回溯)。
沒有原子性的組將允許回溯——它仍然會找到第一個序列,然后如果前面的匹配失敗,它將回溯並找到下一個序列,直到找到整個正則表達式的匹配或所有可能性都用盡。
例子
輸入字符串: bbabbbabbbbc
模式:/(?>. /(?>.*)c/
由於貪婪量詞*
, .*
的第一個匹配是bbabbbabbbbc
。 它將保持這個匹配,不允許c
匹配。 匹配器將在字符串末尾的下一個位置重試,並且會發生同樣的事情。 所以沒有任何東西與正則表達式匹配。
輸入字符串: bbabbbabbbbc
模式: /((?>.*)|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
使用原子分組, .*
所有可能性都被切斷並僅限於第一個匹配項。 因此,在貪婪地吃掉整個字符串並且無法匹配之后,引擎必須尋找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
隨后的比賽將從這里繼續。
我最近不得不向其他人解釋原子組,我想我會在這里調整和分享這個例子。
考慮/the (big|small|biggest) (cat|dog|bird)/
匹配以粗體顯示
對於第一線,正則表達式引擎會發現the
。 然后它會繼續我們的形容詞( big
, small
, biggest
),它發現big
。 匹配big
,它繼續查找空間。 然后它查看我們的寵物( cat
、 dog
、 bird
),找到cat
,跳過它,然后找到dog
。
對於第二行,我們的正則表達式會發現the
。 它會繼續查看big
,跳過它,查看並找到small
。 它找到了空間,跳過了cat
和dog
因為它們不匹配,然后找到了bird
。
對於第三行,我們的正則表達式會發現the
,它仍在繼續,並發現big
相匹配的直接要求,並且前進。 它找不到空間,所以它回溯(將位置倒回到它所做的最后一個選擇)。 它跳過big
,跳過small
,並找到也符合直接要求的biggest
。 然后它找到空間。 它跳過cat
並匹配dog
。
對於第四行,我們的正則表達式會發現the
。 它將繼續查看big
,跳過它,查看並找到small
。 然后它找到空間。 它查看並匹配cat
。
考慮/the (?>big|small|biggest) (cat|dog|bird)/
注意形容詞上的?>
原子組。
匹配以粗體顯示
對於第一行、第二行和第四行,我們將得到相同的結果。
對於第三行,我們的正則表達式會發現the
,它延續並找到big
相匹配的直接要求,並且前進。 它找不到空間,但是作為引擎做出的最后選擇的原子組不允許重新檢查該選擇(禁止回溯)。 既然它不能做出新的選擇,匹配就必須失敗,因為我們的簡單表達式沒有其他選擇。
這只是一個基本的總結。 引擎不需要查看整個cat
就知道它不匹配dog
,只需查看c
就足夠了。 當試圖匹配鳥時, cat
的c
和狗中的d
足以告訴引擎檢查其他選項。
但是,如果你有 ... ((cat|snake)|dog|bird)
,引擎當然也需要在它下降到前一組並檢查狗和鳥之前檢查蛇。
還有很多選擇是引擎無法決定的,除非經過看起來不匹配的事情,這就是導致回溯的原因。 如果您有((red)?cat|dog|bird)
,引擎將查看r
,然后退出,注意?
量詞,忽略子組(red)
,並尋找匹配項。
“原子組”是正則表達式永遠不會回溯過去的組。 所以在你的第一個例子中/a(?>bc|b)c/
如果組中的bc
交替匹配,那么它永遠不會回溯並嘗試b
交替。 如果您稍微更改第一個示例以匹配"abcdabcc"
那么您會看到它仍然匹配字符串末尾的"abcc"
而不是開頭的"abc"
。 如果您不使用原子組,那么它可以回溯過去bc
並嘗試b
交替並最終匹配開始時的"abc"
。
至於第二個問題,它有什么不同,這只是你第一個問題的改寫。
最后,原子組不被“稱為”非捕獲組。 這不是他們的替代名稱。 非捕獲組是不捕獲其內容的組。 通常,當您將正則表達式與字符串進行匹配時,您可以檢索所有匹配的組,並且如果您使用替換,您可以在替換中使用反向引用(如\\1
以在那里插入捕獲的組。 但是非捕獲組不提供此功能。 經典的非捕獲組是(?:pattern)
。 原子團恰好也具有非捕獲性,因此它被稱為非捕獲性基團。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.