2011-11-25 64 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這個答案是有點懶惰,我希望它仍然可以幫助那些遇到這個問題沒有一個很好的解釋爲什麼會發生錯誤的人。