簡體   English   中英

這個遞歸正則表達式究竟是如何工作的?

[英]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必須匹配。

但根據regular-expressions.info

(?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不能作為一個整體匹配,但作為oooooo

同樣,每個遞歸級別都是原子級的。 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級,
  • 整個第6個遞歸級別也失敗,因為它無法匹配必要的o s數量
  • 這一直持續到可以匹配必要數量的o的水平。

請參閱正則表達式調試器

在此輸入圖像描述

這或多或少都是Wiktors回答的后續內容 - 即使刪除了邊界一詞,我也很難弄清楚為什么oooooo (6)被匹配為oooooo ,而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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM