2015-01-09 119 views
3

我碰到了一個奇怪的問題。看來我正在達到某種極限與preg_replace函數試圖使用PHP-5.3.3當使用兩個匹配項時,preg_match似乎達到極限

// works fine 
$pattern_1 = '?START(.*)STOP?'; 
$string = 'START' . str_repeat('x',9999999) . 'STOP' ; 
preg_match($pattern_1, $string , $matchedArray)  ; 

$pattern_2 = '?START-ONE(.*)STOP-ONE.*START-TWO(.*)STOP-TWO.*?'; 

// works fine 
$string = 'START-ONE this is head stuff STOP-ONE START-TWO' . str_repeat('x', 49970) . 'STOP-TWO' ; 
preg_match($pattern_2, $string , $matchedArray_2)  ; 

// didnt work 
$string = 'START-ONE this is head stuff STOP-ONE START-TWO' . str_repeat('x', 49971) . 'STOP-TWO' ; 
preg_match($pattern_2, $string , $matchedArray_3)  ; 

只有一個匹配的第一個選項使用一個非常大的字符串,也沒有問題,用了兩場比賽。

第二個選項的字符串長度爲50,026,並且正常工作。最後一個選項的字符串長度爲50027(多一個),匹配不再起作用。由於49971號碼在發生錯誤時可能會發生變化,因此可以將其更改爲更大的值以模擬問題。

任何想法或想法?也許這是一個PHP版本的問題?也許一個可能的解決方法是隻使用一個匹配而不是兩個,然後運行preg_match兩次?

+0

您正在達到內存限制;嘗試在你的'PHP.ini'中增加它。 – 2015-01-09 20:07:57

回答

2

好吧,PHP的正則表達式錯誤不是很健談,它只是返回false爲最後一種情況,它只是告訴比發生錯誤,根據PHP docs

我已經在C#中使用PCRE(preg_match使用的正則表達式引擎)重現了該問題(但字符數更多),並且我得到的錯誤是PCRE_ERROR_MATCHLIMIT

這意味着你正在達到PCRE的回溯極限。這只是一種防止引擎無限循環的安全措施,我認爲您的PHP配置將其設置爲較低的值。

要解決這個問題,你可以設置爲控制這些限制pcre.backtrack_limit PHP選擇一個較高的值:

ini_set("pcre.backtrack_limit", "10000000"); // Actually, this is PCRE's default 

在一個側面說明:

  • 你或許應該用(.*?)取代(.*)以獲得較少的無用回溯和正確性(否則正則表達式引擎將通過STOP字符串,將不得不回溯到達它)
  • 使用?作爲模式分隔符是錯誤的想法,因爲它阻止您使用?元字符,因此應用上述建議。真的,你應該從來沒有使用正則表達式元字符作爲模式分隔符。

如果你想了解更多的低層次的細節,這裏的PCRE文檔(重點煤礦)的相關位:

match_limit領域提供了防止PCRE從手段在運行不匹配的模式時,會佔用大量資源,但在搜索樹中存在大量可能性。經典示例是使用嵌套無限重複的模式。

在內部,pcre_exec()使用稱爲match()的函數,它重複調用(有時遞歸)。match_limit設置的限制是在匹配過程中調用此函數的次數,對限制可發生的回溯量的影響。對於未錨定的模式,計數從主題字符串中每個位置的零開始重新計數。

pcre_exec()被一個已經用JIT選項成功研究過的模式調用時,匹配的執行方式完全不同。然而,在很長一段時間內仍有失控匹配的可能性,因此在這種情況下(但以不同的方式)也使用值來限制匹配可以繼續的時間。

可以在PCRE構建時設置限制的默認值;默認默認爲1000萬,它可以處理除極端情況以外的所有情況。通過向pcre_extra塊提供pcre_exec()來替代缺省值,其中設置了match_limit,並且在flags字段中設置了PCRE_EXTRA_MATCH_LIMIT。如果超出限制,pcre_exec()返回PCRE_ERROR_MATCHLIMIT

一種用於匹配限值也可以通過一個項目在形式

(*LIMIT_MATCH=d) 

其中d是一個十進制數的模式的開始來提供。但是,除非d小於調用者設置的限制pcre_exec(),否則將忽略此設置,或者如果未設置此限制,則小於默認設置。

+0

你關於避免元字符的觀點是**優秀**,謝謝你指出我。我實際上正在閱讀網頁,並試圖解析出身體部分,因此需要括號(但我打賭你會告訴我,PHP有這樣的功能)。僅僅爲了一個程序而改變php.ini總是感覺有點奇怪。我現在只是使用兩個preg_matches,而不會嘗試你的建議。再次感謝。 – edwardsmarkf 2015-01-09 22:29:38

+0

不客氣。是的,你應該使用[更好的工具](http://stackoverflow.com/a/3577662/3764814),因爲用正則表達式解析HTML [不適合每個人](http://stackoverflow.com/a/1732454/3764814 ):) – 2015-01-09 23:34:48

+0

此外,'ini_set'僅爲當前請求更改值,它不是永久的(它不會更改php.ini)。所以你可以繼續使用它。 – 2015-01-09 23:39:39