繁体   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