php - 为什么这个正则表达式将我的字符串截断为零长度?

时间:2009-09-26 06:44:25

标签: php

昨天我追踪了一个奇怪的错误,导致网站只显示一个白页 - 没有内容,没有错误信息可见。

我发现preg_replace中使用的正则表达式是问题所在。

我使用了正则表达式,以便在回显html之前替换累积内容中的标题html标记。 html在发生错误的页面上变得相当大(60 kb - 不是太大)并且看起来像preg_replace /使用的正则表达式只能处理一定长度的字符串 - 或者我的正则表达式真的搞砸了(也可能)。

看看这个重现问题的示例程序(在PHP 5.2.9上测试):


function replaceTitleTagInHtmlSource($content, $replaceWith) {
  return preg_replace('#(<title>)([\s\S]+)(<\/title>)#i', '$1'.$replaceWith.'$3', $content);
}


$dummyStr = str_repeat('A', 6000);

$totalStr = '<title>foo</title>';

for($i = 0; $i < 10; $i++) {
  $totalStr .= $dummyStr;
}

print 'orignal: ' . strlen($totalStr);
print '<hr />';

$replaced = replaceTitleTagInHtmlSource($totalStr, 'bar');

print 'replaced: ' . strlen($replaced);
print '<hr />';

输出:

orignal:60018
已取代:0

所以 - 函数获取一个长度为60000的字符串,并返回一个长度为0的字符串。不是我想用我的正则表达式做的。


更改

for($i = 0; $i < 10; $i++) {

for($i = 0; $i < 1; $i++) {

为了减少总字符串长度,输出为:

orignal:6018
替换:6018


删除替换后,页面内容显示没有任何问题。

4 个答案:

答案 0 :(得分:3)

好像你正在遇到backtracking limit

如果您打印preg_last_error(),则会确认:它会返回PREG_BACKTRACK_LIMIT_ERROR

您可以增加ini文件中的限制,也可以使用ini_set()或将正则表达式从([\s\S]+)更改为.*?,这样可以阻止它回溯太多。

答案 1 :(得分:1)

之前已经多次说过,例如Regex to match the first ending HTMl tag(并且可能会再次提到)正则表达式不适合HTML,因为标签过于不规则。

在可用的DOM功能中使用它们。

答案 2 :(得分:1)

回溯:[\s\S]+将匹配所有可用字符,然后返回查找</title>的字符串。 [^<]+匹配所有不是<的字符,因此可以更快地抓取</title>

function replaceTitleTagInHtmlSource($content, $replaceWith) {
  return preg_replace('#(<title>)([^<]+)(</title>)#i', '$1'.$replaceWith.'$3', $content);
}

答案 3 :(得分:0)

你的正则表达式似乎有点滑稽。

([\ s \ S] +)匹配所有空格和非空格。你应该尝试(。*?)。

改变你的功能对我有用:

function replaceTitleTagInHtmlSource($content, $replaceWith) {
  return preg_replace('`\<title\>(.*?)\<\/title\>`i', '<title>'.$replaceWith.'</title>', $content);
}

问题似乎是你试图用$ 1和$ 3来匹配和