PHP抛出PREG_BACKTRACK_LIMIT_ERROR错误但正则表达式在javascript中工作,为什么?

时间:2014-07-02 04:55:10

标签: php regex

这是正则表达式:

([A-Z]+(?:(?:[\w\d@#$%&;,\+\.\*!-]+)?\s?(?![a-z&;]+\s)){1,})\s(<span class="label label-inverse">.*?</span>)

这将匹配如下字符串:

<p>Buy Apple <span class="label label-inverse">AAPL - NYSE</span> at or under $100 per share.</p>

在javascript正则表达式测试程序(如regexpal)中测试时,它可以按预期工作。但是,PHP preg_replace函数返回null,错误为PREG_BACKTRACK_LIMIT_ERROR。

$string = preg_replace('~([A-Z]+(?:(?:[\w\d@#%&!;,\+\.\*\$-]+)?\s?(?![a-z&;]+\s)){1,})\s(<span class="label label-inverse">.*?</span>)~i', '<strong>$1</strong> $2', $string);

这是为什么?任何人都可以解释PHP中的正则表达式有什么问题,以及如何纠正它,以便它像在javascript中一样运行?

2 个答案:

答案 0 :(得分:0)

这是一个丑陋的表达。你听说过用正则表达式解析html的警告,对吧?

  • {1,}可以替换为+
  • [\w\d@#$%&;,\+\.\*!-]可以替换为[\w@#$%&;,+.*!-]
  • (?:... +)?中的量词可以替换为*

坦率地说,我没有理由不将其简化为:

([A-Z]+[\w@#$%&;,+.*!\s-]+)\s(<span class="label label-inverse">.*?</span>)

甚至更进一步。

the demo中,查看右侧窗格中的捕获组。

答案 1 :(得分:0)

回溯限制通常是由于表达效率低下造成的;你应该简化它:

~([\w\s\d@#$%&;,+.*!-]+)(?=<span class="label label-inverse">[^<]+</span>)~i'

如果后跟<span>标记,它会捕获一个单词(或多个单词);未捕获前瞻断言,因此它使替换模式更简单。

$expr = '~([\w\s\d@#$%&;,+.*!-]+)(?=<span class="label label-inverse">[^<]+</span>)~i';
$string = preg_replace($expr, '<strong>$1</strong>', $string);

Demo

似乎规则可以描述为:

  1. <span>
  2. 之前匹配至少一个标题名称
  3. <span>
  4. 之前只匹配一个非标题的名称

    所以:

    ~((?:[A-Z]\S*\s)+|\S+\s)(?=<span class="label label-inverse">[^<]+</span>)~