这是this question的后续内容。
看看这种模式:
(o(?1)?o)
它匹配o
的任何序列,长度为2 n ,n≥1。
It works, see 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 ,因为我会将模式描述为* an未定义的o o
数量,相互堆叠。
可视化:
没有递归, 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 does mention the technique:
如果您在其呼叫的组内发出呼叫,您将拥有一个递归捕获组。
答案 0 :(得分:4)
您正确了解递归。词边界让你感到困惑。模式周围的\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
请参阅regex demo without word boundaries。
为什么在上面的例子中添加(?>...)
? Each recursion level in PHP recursive regexes is atomic,与Perl 不同,一旦前一级别失败,引擎就不会去回到下面的那个。
当您添加字边界时,匹配的第一个o
和最后o
在之前/之后不能包含任何其他字词。那么,ooo
won't match那么。
请参阅rexegg.com上逐步解释的Recursive Regular Expressions和Word Boundary: \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
s。请参阅regex debugger:
答案 1 :(得分:2)
这或多或少都是Wikors回答的后续行动 - 即使在删除单词边界后,我也很难弄清楚为什么oooooo
(6)匹配为oooo
和{{1 } {} oo
(7)匹配为ooooooo
。
以下是它的详细工作原理:
扩展递归模式时,内部递归是原子的。使用我们的模式,我们可以将其展开到
oooooo
(在实际模式中,这个获得再次展开,但这不会改变解释)
以下是字符串的匹配方式 - 首先(?>o(?>o(?>o(?>o(?>oo)?o)?o)?o)?o)
(6)
oooooo
现在(?>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)
(7)
ooooooo