在回溯和失败之后起作用的动词

27

我最近在阅读 PCRE(Perl兼容正则表达式)文档时,发现了一些有关正则表达式的有趣技巧。继续阅读并感到疲惫后,由于使用一些(*...)模式时存在一些困惑,我停了下来。

我的问题和困惑与(*PRUNE)(*FAIL)有关

现在作为参考,(*SKIP)的行为类似于(*PRUNE),但如果模式未定位,则向前推进不是到下一个字符,而是到遇到(*SKIP)位置在主题中的位置。

文档说明(*PRUNE)会导致匹配失败,如果其余部分的模式不匹配,这种失败会发生在主题中当前的起始位置。它还说明(*FAIL)(?!)否定断言同义。在模式中强制在给定位置处匹配失败。

因此,(*FAIL)基本上像是一个失败的否定断言,并且是(?!)的同义词。

(*PRUNE)会导致匹配失败,如果存在后面的匹配失败,并且需要回溯才能达到它。

当涉及到失败点时,它们有何不同?

有人可以提供如何正确实现和使用这些内容的示例吗?


3
在你关注这种元素之前,我建议你应该完全精通Perl正则表达式的高级水平。此外,如果你选择使用这种奥秘的功能,你应该有一个非常好的理由,并准备承担任何阅读或维护你的代码的人的憎恶。正则表达式吸引人,但往往被迫执行其他工作。PCRE在许多方面都存在不足,它的使用应该局限于由更高级别结构驱动的琐碎实例。 - Borodin
此问题已添加到Stack Overflow正则表达式FAQ,位于“高级Regex-Fu”下。 - aliteralmind
1个回答

40
在阅读本答案之前,您应该熟悉回溯机制、原子组和贪婪量词的机理。您可以在Friedl书籍中和以下链接中找到有关这些概念和特性的信息:www.regular-expressions.infowww.rexegg.com 所有测试都是使用全局搜索(使用preg_match_all()函数)进行的。

(*FAIL)(或缩写形式(*F))

baabo caaco daado

caac(*FAIL)|aa.|caaco|co

[0] => aab
[1] => caaco
[2] => aad

(*FAIL)会导致与模式中的“坏字符”完全相同的行为。例如,如果您用“R”替换它,则会得到完全相同的结果:caacR|aa.|caaco|co。更通用的做法是,您可以使用“总是失败的子模式”(如(?!)(?=a(?<!a))等)来替换(*FAIL)

a(来自“baabo”的第一个):毫不意外,第一个结果由第二个选择项找到。(aab

c(第一个):正则表达式引擎遇到第一个“c”,尝试第一个选择项并找到:caac,但强制使子模式失败。然后正则表达式引擎(始终从第一个“c”开始)尝试失败的第二个选择项,第三个选择项成功。(caaco

a(来自“daado”的第一个):第三个结果由第二个选择项找到。(aad

(*SKIP)

baabo caaco daado

caa(*SKIP)c(*FAIL)|aa.|caaco|co

[0] => aab
[1] => co
[2] => aad

这个动词定义了一个点,当子模式后面失败时,正则表达式引擎不允许回溯。因此,所有在子模式中找到的字符都会被一次性地消耗掉,并且不能用于模式(替代方案)的其他部分

a(来自“baabo”的第一个):第二个替代方案找到了第一个结果。(aab

c(第一个):正则表达式引擎像第一种情况一样找到了caac,然后失败(因为(*FAIL)动词),回溯到第二个“c”,但不允许回溯到先前匹配的字符("caa")在(*SKIP)动词之前。
c(第二个):现在,正则表达式引擎总是尝试第一个替代方案,但在这个新位置上失败了,因为后面跟着一个“o”,而不是一个“a”,然后它回溯到这个第二个“c”。请注意,在这种情况下,这些字符不像以前那样被消耗,因为子模式已经在到达(*SKIP)动词之前失败了。 测试第二个替代方案失败了(不以“c”开头)。第三个替代方案也失败了,因为下一个字符是“o”,而不是“a”。 第四个替代方案成功并给出第二个结果。(co

a(来自“daado”的第一个):第二个替代方案找到了第三个结果。(aad

(*PRUNE)

baabo caaco daado

caa(*PRUNE)c(*FAIL)|aa.|caaco|co

[0] => aab
[1] => aac
[2] => aad

这个动词与(*SKIP)不同,因为它不禁止使用所有先前匹配的字符,而是跳过子模式中第一个匹配的字符(或禁止子模式以此开始),如果子模式后面将失败。

a (来自"baabo"的第一个):第一个结果由第二个备选项找到。(aab)

c (第一个):正则表达式引擎像第一种情况一样找到caac,然后失败,但现在回溯到从"caaco"开始的第一个"a",因为第一个"c"被跳过了。
a (来自"caaco"的第一个):尝试第一个备选项失败,第二个成功并给出第二个结果。(aac)

a (来自"daado"的第一个):第三个结果由第二个备选项找到。(aad)


1
非常感谢您提供如此详细的答案,这解决了我的困惑。 - hwnd
1
@hwnd:关于这个主题,pcre文档确实缺乏示例。 - Casimir et Hippolyte
请随意在此回答。http://stackoverflow.com/questions/19437211/special-construct-of-expression-use - hwnd
2
太酷了,这是我的第一个赏金!感谢 @HamZa。 - Casimir et Hippolyte
3
太悲哀了,这个优秀的答案只被浏览了370次! - Eathen Nutt
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接