提问人:HamZa 提问时间:4/25/2015 最后编辑:CommunityHamZa 更新时间:4/26/2015 访问量:3785
将 n> 0 的 a^n b^n c^n 与 PCRE 匹配
Matching a^n b^n c^n for n > 0 with PCRE
问:
如何将 n > 0 的 a^n b^n c^n 与 PCRE 匹配?
以下情况应匹配:
abc
aabbcc
aaabbbccc
以下情况不应匹配:
abbc
aabbc
aabbbccc
这是我“尝试过”的; 但这与 n > 0 的 a^n b^n 匹配:/^(a(?1)?b)$/gmx
ab
aabb
aaabbb
注意:这个问题与这个问题相同,只是语言发生了变化。
答:
首先,让我们解释一下你所拥有的模式:
^ # Assert begin of line
( # Capturing group 1
a # Match a
(?1)? # Recurse group 1 optionally
b # Match b
) # End of group 1
$ # Assert end of line
使用以下修饰符:
g: global, match all
m: multiline, match start and end of line with ^ and $ respectively
x: extended, indentation are ignored with the ability to add comments with #
递归部分是可选的,以便最终退出“无休止”递归。
我们可以使用上面的模式来解决问题。我们需要添加一些正则表达式来匹配零件。问题是当匹配时,它已经被消耗了,这意味着我们无法追溯。c
aabb
aabbcc
解决方案是什么?使用前瞻!Lookaheads 是零宽度的,这意味着它不会消耗和前进。一探究竟:
^ # Assert begin of line
(?= # First zero-with lookahead
( # Capturing group 1
a # Match a
(?1)? # Recurse group 1 optionally
b # Match b
) # End of group 1
c+ # Match c one or more times
) # End of the first lookahead
(?= # Second zero-with lookahead
a+ # Match a one or more times
( # Capturing group 2
b # Match b
(?2)? # Recurse group 2 optionally
c # Match c
) # End of group 2
) # End of the second lookahead
a+b+c+ # Match each of a,b and c one or more times
$ # Assert end of line
基本上,我们首先断言存在 a^n b^n,然后我们断言 b^n c^n,这将导致 a^n b^n c^n。
评论
a+b+c+
Qtax技巧
(强大的自参捕捕获组)
^(?:a(?=a*(\1?+b)b*(\2?+c)))+\1\2$
这个解决方案也被命名为“Qtax 技巧”,因为它使用的技术与 Qtax 在 ASCII“图像”中的“垂直”正则表达式匹配相同的技术。
所讨论的问题归结为需要断言三个组是相同长度的匹配。作为简化版本,为了匹配:
xyz
其中 和 实际上只是一个具有匹配长度为 的变量的子模式。使用具有自引用捕获组的 lookahead 的表达式,我们指定的字符会添加到 lookahead 的每次重复中,该字符可以有效地用于“计数”:x
y
z
n
a
b
c
aaabbbccc ^ ^ ^
这是通过以下方式实现的:
(?:a…)+
子模式的字符匹配。使用 ,我们直接跳到“计数器”。a
(?=a*
(\1?+b)
捕获组 () 有效地消耗以前匹配的任何内容(如果存在),并使用不允许回溯的所有格匹配,如果计数器不同步,则匹配失败 - 也就是说,子模式多于子模式。在第一次迭代中,这不存在,并且没有匹配的内容。然后,匹配子模式的字符。它被添加到捕获组中,有效地“计数”组中的b
之一。使用 ,我们直接跳到下一个“计数器”。\1
b
a
b
b*
(\2?+c)
捕获组 () 会有效地消耗之前匹配的任何内容,就像上面一样。由于此附加字符捕获的工作方式与上一组相同,因此允许字符在这些字符组中同步长度。假设连续序列为:\2
a..b..c..
(请原谅我的艺术。
第一次迭代:
| The first 'a' is matched by the 'a' in '^(?:a…)'.
| The pointer is stuck after it as we begin the lookahead.
v,- Matcher pointer
aaaa...bbbbbbbb...cccc...
^^^ |^^^ ^
skipped| skipped Matched by c in (\2?+c);
by a* | by b* \2 was "nothing",
| now it is "c".
Matched by b
in (\1?+b).
\1 was "nothing", now it is "b".
第二次迭代:
| The second 'a' is matched by the 'a' in '^(?:a…)'.
| The pointer is stuck after it as we begin the lookahead.
v,- Matcher pointer
aaaa...bbbbbbbb...cccc...
/|^^^ |^
eaten by| skipped |Matched by c in (\2?+c);
\1?+ | by b* | '\2' was "nothing",
^^ | \2?+ now it is "cc".
skipped|
by a* \ Matched by b
in (\1?+b).
'\1' was "nothing", now it is "bb".
由于上面讨论的三个组分别“消耗”了 、 中的一个,因此它们以循环方式匹配,并分别由 和 组“计数”。通过额外的锚定和捕获我们开始的内容,我们可以断言我们分别匹配 、 和 和 和 (表示上面的每个组)。a
b
c
(?:a…)+
(\1?+b)
(\2?+c)
xyz
x
y
z
an
bn
cn
作为奖励,要“数”更多,可以这样做:
Pattern: ^(?:a(?=a*(\1?+b)b*(\2?+c)))+\1{3}\2$ Matches: abbbc aabbbbbbcc aaabbbbbbbbbccc
Pattern: ^(?:a(?=a*(\1?+bbb)b*(\2?+c)))+\1\2$ Matches: abbbc aabbbbbbcc aaabbbbbbbbbccc
评论