【问题标题】:PHP throws PREG_BACKTRACK_LIMIT_ERROR error but regex works in javascript, why?PHP 抛出 PREG_BACKTRACK_LIMIT_ERROR 错误但正则表达式在 javascript 中有效,为什么?
【发布时间】:2014-07-02 04:55:09
【问题描述】:

这是正则表达式:

([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>

在 regexpal 等 javascript 正则表达式测试器中进行测试时,它按预期工作。但是,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 中一样运行?

【问题讨论】:

  • 在 PHP 中,您是否针对较大的文档对其进行测试?
  • 顺便说一句,你是怎么想出这种可怕的表达方式的?你想精确匹配什么?
  • 我在 JS 和 PHP 中针对相同的 HTML 对其进行了测试。如果可以优化正则表达式,我会全神贯注。它需要匹配大型 HTML 文档中的以下 HTML 字符串:Apple &lt;span class="label label-inverse"&gt;AAPL - NYSE&lt;/span&gt;eBay &lt;span class="label label-inverse"&gt;EBAY - NYSE&lt;/span&gt;E*TRADE &lt;span class="label label-inverse"&gt;ETFC - NYSE&lt;/span&gt;

标签: php regex


【解决方案1】:

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

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

如果后面跟着&lt;span&gt; 标签,它会捕获一个单词(或多个单词);未捕获前瞻断言,因此它使替换模式更简单。

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

Demo

好像规则可以这样描述:

  1. &lt;span&gt; 之前至少匹配一个标题大小写名称
  2. &lt;span&gt; 之前精确匹配一个非标题大小写名称

所以:

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

【讨论】:

  • 这在 PHP 中适用于我。似乎我的消极前瞻效率低下,用积极前瞻代替它解决了这个问题?
  • @Slim 我认为这也是 {1,} 乘数内的嵌套;我发现很难推断出创作时的逻辑;-)
  • 你的正则表达式改变了我原来的一些匹配项。示例:&lt;p&gt;Hello this is Ford Motor Company &lt;span class="label label-inverse"&gt;F - NYSE&lt;/span&gt; at or under $100 per share.&lt;/p&gt; 它应该匹配强标签中的“福特汽车公司”。 &lt;p&gt;Hello this is eBay &lt;span class="label label-inverse"&gt;EBAY - NYSE&lt;/span&gt; at or under $100 per share.&lt;/p&gt; 它应该匹配强标签中的“eBay”。 &lt;p&gt;Hello this is E*TRADE &lt;span class="label label-inverse"&gt;ETFC - NYSE&lt;/span&gt; at or under $100 per share.&lt;/p&gt; 应该匹配强标签中的“E*TRADE”。
  • @Slim 嗯,“eBay”是一个有趣的例外;还有更多吗?
  • @Slim 我更新的答案给出了你给出的正确结果;让我知道你的效果如何。
【解决方案2】:

这是一个丑陋的表达。而且您已经听说过有关使用正则表达式解析 html 的警告,对吧?

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

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

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

甚至更远。

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

【讨论】:

  • FYI 添加了简化正则表达式的演示。
  • 这适用于您的 JS 示例,但在 PHP 中会引发此错误:PHP Error[2]: preg_replace(): Compilation failed: invalid range in character class at offset 23
  • 请再试一次。在大版本中,我忘记移动-。固定。
  • 这是原始问题中发布的 preg_replace() 行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-12
  • 1970-01-01
相关资源
最近更新 更多