PHP - preg_match_all 没有搜索完整的字符串?
Posted
技术标签:
【中文标题】PHP - preg_match_all 没有搜索完整的字符串?【英文标题】:PHP - preg_match_all not searching the full string? 【发布时间】:2012-07-21 00:11:53 【问题描述】:我正在使用 preg_match_all 搜索我正在读取的文件。该文件包含以下格式的许多行,我正在提取标签之间的数字;
<float_array id="asdfasd_positions-array" count="6">1 2 3 4 5 6</float_array>
我正在使用 preg_match_all 并且它运行良好 - 除了它通过文件这么远然后似乎停止了。
preg_match_all("/\<float_array id\=\".+?positions.+?\" count\=\".+?\"\>(.+?)\<\/float_array\>/",$file, $results);
该文件有 90,000 行,大小约为 8MB。我正在编辑提取的字符串中的每三个数字,并使用 str_replace 将其编辑回文件中。然后再次写入该文件。在此处查看完整脚本;
http://pastie.org/4300537
脚本成功地替换了大约一半的条目,并且对文件的后半部分没有做任何事情。我什至从文件的较高位置复制了一个成功编辑的行并进一步向下粘贴......并且它没有在文件中进一步编辑。就好像数组已满但 memory_limit 设置为 500M。
有什么想法吗?
编辑:找到解决方案
我发现了问题 - 在某些情况下,标签之间的字符串太大而被跳过。我在 PHP 中找到了限制。 pcre.backtrack_limit 设置为 100000 并且某些字符串大于此值。所以我使用以下行在 .htaccess 文件中增加了它,现在它可以工作了。
php_value pcre.backtrack_limit 5000000
【问题讨论】:
您是否将 PHP 执行时间限制设置为 0?大约 30 秒后,脚本将关闭,除非您指定它根据需要运行。 文件正在完全读入,因为$file
字符串最后被写入文件,并且完整的文件就在那里。脚本完全执行,我正在重置循环内的超时。如果我在最后一行回显就好了。
反斜杠太多(<
和 =
不需要)。也使用单引号。并进一步限制格式[\w-]+
或\d+
和[\d\s]*
代替所有.+?
。如果它是有效的 XML,也可以尝试 SimpleXML;简单得多,而且速度也不会慢很多。
感谢马里奥 - 更典型的 ID 类似于“10iHdUVMXDPhBIJhh1IGZa-positions-array”。你的建议会涵盖“”和“-”字符吗?这些字符的数量和位置也各不相同。
【参考方案1】:
如果内存是个问题而不是执行时间限制,那么使用慢速解决方案(逐行)>>
$fi = fopen("data.txt", "r");
$fo = fopen('data2.txt', 'w');
while (!feof($fi))
$line = fgets($fi);
# regex stuff here
fwrite($fo, $line);
fclose($fi);
fclose($fo);
【讨论】:
为什么内存会导致它 - 我认为 memory_limit 是数组或字符串长度的唯一限制因素? @user1107685 - 还有什么问题?如果脚本适用于第一半,那么它也应该适用于第二半。在大多数情况下,执行时间限制和/或内存在这之后,所以...尝试一下 - 如果它现在不起作用,那么导致它的原因不是记忆...简单! 标签不一定会在行边界处停止;您最好使用preg_match
并指定PREG_OFFSET_CAPTURE
,这样您就可以分别处理每个结果。这确实需要 8 MiB 的文件缓存,但您会得到更好的处理。除此之外,标签不仅会跨越行边界,整个 XML 可以包含在单个 8 MiB 行中。或者,解析 XML,然后解析返回的浮点数组。
我现在正在尝试逐行方法。每条线都有 1 个标签,所以这不是问题,它们不会跨越线的边界。有兴趣看看这也慢了多少。
这主要是有效的——实际上比以前的代码快得多——但它仍然跳过了一些。它会跳过标签之间数字最多的那些,大约 14,000 个数字。【参考方案2】:
您可以考虑使用像这样的简单解析器解析您的文本文件>>
$fi = fopen("data.txt", "r");
$fo = fopen('data2.txt', 'w');
$status = 0;
do
$data = stream_get_line($fi, PHP_INT_MAX, ">");
if ($status == 1)
preg_match("/(.*)<\/float_array$/", $data, $m);
$status--;
if (sizeof($m) != 0)
fwrite($fo, $m[1] . "\n");
continue;
if ($status == 0)
preg_match("/<float_array[^>]*?\bid\s*=\s*[\"'][^\"']*?positions[^\"']*?[\"'][^>]*?\bcount\s*\=[^>]*?$/", $data, $m);
if (sizeof($m) > 0)
$status++;
while (!feof($fi));
fclose($fi);
fclose($fo);
【讨论】:
以上是关于PHP - preg_match_all 没有搜索完整的字符串?的主要内容,如果未能解决你的问题,请参考以下文章
preg_match_all (PHP) 中的 UTF-8 字符