2011-11-25 63 views
3

我在preg_match_all中使用了很长的模式。PHP preg_match_all限制

时运行的代码,我得到这个错误:

警告:preg_match_all():编译失败:正则表达式太大的偏移707830

搜索后,我得到了解决,所以我应该增加pcre.backtrack_limitpcre.recursion_limit的值php.ini

但是,当我增加值并重新启动我的apache后,它仍然有同样的问题。我的PHP版本是5.3.8

+9

请发布您正在使用的正则表达式。 –

回答

8

增加PCRE回溯和递归限制可能会解决问题,但在数据大小达到新限制时仍会失败。 (没有更多的数据很好地扩展)

例如:

<?php 
// essential for huge PCREs 
ini_set("pcre.backtrack_limit", "23001337"); 
ini_set("pcre.recursion_limit", "23001337"); 
// imagine your PCRE here... 
?> 

真正解决根本问题,必须优化你的表达和(如果可能的话)你的复杂的表达式分割成“零件”和移动一些逻辑给PHP。我希望你通过阅读这个例子来了解这个想法..而不是试图直接用一个PCRE来找到子结构,我演示了一种更“迭代”的方法,使用PHP进行更深入和更深入的结构。例如:

<?php 
$html = file_get_contents("huge_input.html"); 

// first find all tables, and work on those later 
$res = preg_match_all("!<table.*>(?P<content>.*)</table>!isU", $html, $table_matches); 

if ($res) foreach($table_matches['content'] as $table_match) { 

    // now find all cells in each table that was found earlier .. 
    $res = preg_match_all("!<td.*>(?P<content>.*)</td>!isU", $table_match, $cell_matches); 

    if ($res) foreach($cell_matches['content'] as $cell_match) { 

     // imagine going deeper and deeper into the structure here... 
     echo "found a table cell! content: ", $cell_match; 

    }  
} 
+0

很好的答案,谢谢凯伊 – Ahmad

+0

其实对于我的情况,它自我的模式很长。我已阻止以|分隔的网站例如sex.com | porn.com | bad.com。你的解决方案似乎很好在我尝试将图案分离为较小的部分之后,它运行良好:)谢谢Kaii – Ahmad

11

该错误是不是正则表达式的性能,它是关于正则表达式本身。更改pcre.backtrack_limitpcre.recursion_limit不会产生任何效果,因为正则表达式永远不会有机会运行。问题是,正则表达式太大了,解决方案是使正则表达式更小 - 大大减少了很多。

+2

好的答案,谢谢Alan。 – Ahmad

3

我在写这个答案,因为我在同一个问题上做了标记。由于Alan Moore指出调整回溯和递归限制无助于解决问题。

所描述的错误发生在针头超出最大可能的针头大小时,这是由下面的pcre库限制的。所描述的错误是不是由php引起的,而是由底层的pcre库引起的。这是错误信息#20在此规定:

https://github.com/php/.../pcre_compile.c#L477

PHP只是打印它从失败PCRE库收到的ERRORTEXT。

但是,当我尝试使用先前捕获的碎片作为针并且它们大于32k字节时,此错误出现在我的环境中。

它可以很容易地通过使用这个简单的脚本从PHP的CLI

<?php 
// This script demonstrates the above error and dumps an info 
// when the needle is too long or with 64k iterations. 

$expand=$needle="_^b_"; 
while(! preg_match($needle, "Stack Exchange Demo Text")) 
{ 
    // Die after 64 kbytes of accumulated chunk needle 
    // Adjust to 32k for a better illustration 
    if (strlen($expand) > 1024*64) die(); 

    if ($expand == "_^b_") $expand = ""; 
    $expand .= "a"; 
    $needle = '_^'.$needle.'_ism'; 

    echo strlen($needle)."\n"; 

} 
?> 

要修正此错误,可以得到的针必须减少或测试 - 如果需要的一切将被捕获 - 多重的preg_match与额外的偏移量参数必须被使用。

<?php 
    if ( 
     preg_match( 
      '/'.preg_quote( 
        substr($big_chunk, 0, 20*1024) // 1st 20k chars 
       ) 
       .'.*?'. 
       preg_quote( 
        substr($big_chunk, -5) // last 5 
       ) 
      .'/', 
      $subject 
     ) 
    ) { 
     // do stuff 
    } 

    // The match all needles in text attempt 
    if (preg_match( 
      $needle_of_1st_32kbytes_chunk, 
      $subj, $matches, $flags = 0, 
      $offset = 32*1024*0 // Offset -> 0 
     ) 
     && preg_match( 
      $needle_of_2nd_32kbytes_chunk, 
      $subj, $matches, $flags = 0, 
      $offset = 32*1024*1 // Offset -> 32k 
     ) 
     // && ... as many preg matches as needed 
    ) { 
     // do stuff 
    } 

    // it would be nicer to put the texts in a foreach-loop iterating 
    // over the existings chunks 
?> 

你明白了。

Allthough这个答案是有点懒惰,我希望它仍然可以帮助那些遇到这个问题没有一个很好的解释为什么会发生错误的人。