[英]How exactly does this recursive regex work?
這是這個問題的后續內容。
看看這個模式:
(o(?1)?o)
它匹配o
任何序列,長度為2 n ,n≥1。
它有效,請參閱regex101.com (為了更好的演示而添加了單詞邊界)。
問題是: 為什么?
在下文中,字符串的描述(匹配與否)將只是粗體數字或粗體術語,描述長度,如2 n 。
細分(添加空格):
( o (?1)? o )
( ) # Capture group 1
o o # Matches an o each at the start and the end of the group
# -> the pattern matches from the outside to the inside.
(?1)? # Again the regex of group 1, or nothing.
# -> Again one 'o' at the start and one at the end. Or nothing.
我不明白為什么這不匹配2n ,而是2 n ,因為我會將模式描述為*未定義的oo
數,相互堆疊。
可視化:
沒有遞歸, 2是匹配:
oo
一次遞歸, 4是匹配:
o o
oo
到目前為止,這么容易。
兩次遞歸。 顯然是錯誤的,因為模式不匹配6 :
o o
o o
oo
但為什么? 它似乎符合這種模式。
我的結論是,這不僅僅是重復的普通模式,因為否則6必須匹配。
(?P<name>[abc])(?1)(?P>name)
匹配三個字母,如(?P<name>[abc])[abc][abc]
。
和
[abc])(?1){3}
[...]相當於([abc])[abc]{3}
因此,它似乎只是重新匹配正則表達式代碼而沒有關於捕獲組的上一個匹配的信息。
有人可以解釋並可能想象出為什么這個模式匹配2 n而不是別的嗎?
編輯:
評論中提到:
我懷疑引用自身內部的捕獲組實際上是一個支持的案例。
regular-expressions.info確實提到了這個技術:
如果您在其調用的組內部發出呼叫,您將擁有一個遞歸捕獲組。
你正確理解遞歸。 詞邊界讓你感到困惑。 模式周圍的\\b
要求正則表達式引擎僅匹配字符串(如果字符串不在前面並且后跟單詞字符)。
看看遞歸如何:
( o (?1)? o ) => oo
(?1)
然后用(o(?1)?o)
代替:
( o (?>o(?1)?o)? o ) => oo or oooo
然后再說:
(o (?>o(?>o(?1)?o)?o)? o) => oo, oooo, oooooo
為什么在上面的例子中添加(?>...)
? PHP遞歸正則表達式中的每個遞歸級別都是原子的 , 與Perl不同 ,一旦前一級別失敗,引擎就不會返回到下一級別。
當您添加單詞邊界時,匹配的第一個o
和最后一個o
不能在之前/之后具有任何其他單詞chars。 那么, ooo
就不會匹配了 。
請參閱rexegg.com上逐步解釋的遞歸正則表達式和Word邊界: \\b
。
為什么
oooooo
不能作為一個整體匹配,但作為oooo
和oo
?
同樣,每個遞歸級別都是原子級的。 oooooo
匹配如下:
(o(?1)?o)
匹配第一個o
(?1)?
得到擴展,模式現在是(o(?>o(?1)?o)?o)
並且它匹配輸入中的第二個o
(o(?>o(?>o(?>o(?>o(?>o(?>o(?1)?o)?o)?o)?o)?o)?o)?o)
不再與輸入匹配,回溯發生,我們進入第6級, o
s數量 o
的水平。 請參閱正則表達式調試器 :
這或多或少都是Wiktors回答的后續內容 - 即使刪除了邊界一詞,我也很難弄清楚為什么oooooo
(6)被匹配為oooo
和oo
,而ooooooo
(7)被匹配為oooooo
。
以下是它的詳細工作原理:
在擴展遞歸模式時,內部遞歸是原子的。 使用我們的模式,我們可以將其展開
(?>o(?>o(?>o(?>o(?>oo)?o)?o)?o)?o)
(在實際模式中,這個get再次展開,但這不會改變解釋)
以下是字符串的匹配方式 - 首先是oooooo
(6)
(?>o(?>o(?>o(?>o(?>oo)?o)?o)?o)?o)
o |ooooo <- first o gets matched by first atomic group
o o |oooo <- second o accordingly
o o o |ooo <- third o accordingly
o o o o |oo <- fourth o accordingly
o o o o oo| <- fifth/sixth o by the innermost atomic group
^ <- there is no more o to match, so backtracking starts - innermost ag is not matched, cursor positioned after 4th character
o o o o xx o |o <- fifth o matches, fourth ag is successfully matched (thus no backtracking into it)
o o o o xx o o| <- sixth o matches, third ag is successfully matched (thus no backtracking into it)
^ <- no more o, backtracking again - third ag can't be backtracked in, so backtracking into second ag (with matching 3rd 0 times)
o o |oo<oo <- third and fourth o close second and first atomic group -> match returned (4 os)
現在ooooooo
(7)
(?>o(?>o(?>o(?>o(?>oo)?o)?o)?o)?o)
o |oooooo <- first o gets matched by first atomic group
o o |ooooo <- second o accordingly
o o o |oooo <- third o accordingly
o o o o |ooo <- fourth o accordingly
o o o o oo|o <- fifth/sixth o by the innermost atomic group
o o o o oo o| <- fourth ag is matched successfully (thus no backtracking into it)
^ <- no more o, so backtracking starts here, no backtracking into fourth ag, try again 3rd
o o o |ooo<o <- 3rd ag can be closed, as well as second and first -> match returned (6 os)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.