【问题标题】:Why does this regular expression fail with PCRE (PHP < 7.3) but works with PCRE2 (PHP >= 7.3)为什么这个正则表达式在 PCRE (PHP < 7.3) 中失败,但在 PCRE2 (PHP >= 7.3) 中有效
【发布时间】:2021-09-19 17:44:48
【问题描述】:

正则表达式:

/(?<nn>(?!und)[^\/,&;]+)(?:,\s?+)(?<vn>(?1))(?:\/|&|;|und|$)\s?/

应该使用preg_match_all产生两个匹配

nn(1): Oidtmann-van Beek
vn(1): Jeanne 

nn(2): Oidtmann
vn(2): Peter

关于示例字符串Oidtmann-van Beek, Jeanne und Oidtmann, Peter

这适用于 PCRE2 (PHP >= 7.3)。

但 PHP

https://regex101.com/r/zotHZN/1/

【问题讨论】:

  • 我认为像/\b(?&lt;nn&gt;(?!und\b)\w+(?:[-'\s]+(?!und\b)\w+)*),\s?(?&lt;vn&gt;(?&amp;nn))\b/u 这样更精确的模式会更好。见this regex demo
  • 如果没有用于测试的示例字符串,很难判断。我在这个组中看到了一件有趣的事情:(?:,\s?+) — 我不确定 php 会如何处理 \s?+(一个或多个 可选 空格)。我会尝试\s*(零个或多个空格)。这可能对 PCRE 编译器更有意义。
  • @rickdenhaan \s?+ 匹配 1 或 0 个空白,它在 PCRE 和 PCRE2 中都是有效模式。
  • 原因是PCRE2中的子程序模式不是原子的,可以回溯到。

标签: php regex php-7.2 php-7.3


【解决方案1】:

您不会通过 PCRE 获得预期的输出,因为 (?1) 正则表达式子例程是原子的,它的模式不能回溯到。

"Differences in recursion processing between PCRE2 and Perl":

在 10.30 版本之前,PCRE2 中的递归处理与 Perl 的不同之处在于递归子例程调用始终被视为原子组。也就是说,一旦它匹配了一些主题字符串,它就再也不会重新输入,即使它包含未尝试的替代项并且随后出现匹配失败。 (历史记录:PCRE 在 Perl 之前实现了递归。)

从 10.30 版开始,递归子例程调用不再被视为原子。也就是说,如果模式稍后出现匹配失败,可以重新输入它们以尝试未使用的替代方案。这现在与 Perl 的工作方式兼容。如果您希望子例程调用是原子的,则必须将其显式地包含在原子组中。

所以,解决方案是使用模式本身,而不是子程序:

/(?<nn>(?!und)[^\/,&;]+),\s?+(?<vn>(?!und)[^\/,&;]+)(?:\/|&|;|und|$)\s?/

请注意,我只用 \s?+ 替换了 (?:,\s?+),因为这里的非捕获组是多余的。

我认为像/\b(?&lt;nn&gt;(?!und\b)\w+(?:[-'\s]+(?!und\b)\w+)*),\s?(?&lt;vn&gt;(?&amp;nn))\b/u 这样更精确的模式在这里会更好。见this regex demo。它不需要任何回溯,因为\w+(?:[-'\s]+(?!und\b)\w+)* 部分不与,\s? 模式重叠。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-03
    • 1970-01-01
    • 2014-08-02
    • 1970-01-01
    • 2014-02-25
    • 2021-03-04
    • 1970-01-01
    相关资源
    最近更新 更多