php正則失效-最大回溯(pcre.backtrack_limit)/遞迴限制
昨天,同事在處理一個正則採集程式,發現了一個怪現象:在本地正則規則隨便寫都可以匹配到,可是上傳到伺服器後發現正則怎麼寫都匹配不到。自己調整了半天沒有效果,放棄?那就…………,不甘心……,吃過晚飯後,開啟電腦隨便亂狂部落格,還是google了一下,有關正則的規則,找來找去都在談怎麼寫Regex等。最後靜下心來分析了一下。
我和同事的電腦裝的都是wamp,php版本都是5.3.x,伺服器php版本是5.2.x,難道是這個問題,抱著懷疑的心情,開啟了手冊文本處理-pcre函數-preg_match_all,好傢夥原來它上面有一個函數preg_last_error,開啟看了看,那就用他試下My Code,結果返回錯誤是 PREG_BACKTRACK_LIMIT_ERROR ,原來是這個環境變數問題,於是在我代碼的開頭加入ini_set("pcre.backtrack_limit",1000000);結果正常抓取。回頭再看了下手冊,發現下面的介紹:
最後又上網查了一些資料,其中有一個博主寫的挺細的:下面轉一些相關內容,解釋一下:
在PHP的pcre擴充中, 提供了倆個設定項.
1 pcre.backtrack_limit //最大回溯數
2 pcre.recursion_limit //最大嵌套數
預設的backtarck_limit是100000(10萬).
現在要弄清這個問題的原因, 關鍵就是什麼是”回溯”.
這個正則, 使用非貪婪模式, 非貪婪模式比對原理簡單來說是, 在可配也可不配的情況下, 優先不匹配. 記錄備選狀態, 並將匹配控制交給Regex的下一個匹配字元, 當之後的匹配失敗的時候, 再溯, 進行匹配.
舉個例子:
源字串: aaab正則: .*?
匹配過程開始的時候, “.*?”首先取得匹配控制權, 因為是非貪婪模式, 所以優先不匹配, 將匹配控制交給下一個匹配字元”b”, “b”在源字串位置1匹配失敗(“a”), 於是回溯, 將匹配控制交回給”.*?”, 這個時候, “.*?”匹配一個字元”a”, 並再次將控制權交給”b”, 如此反覆, 最終得到匹配結果, 這個過程中一共發生了3次回溯.
最後,還是要說一下
1、在PHP 5.2以後, 提供了:int preg_last_error ( void )Returns the error code of the last PCRE regex execution.
我們應該經常檢查這個函數的返回值, 當不為零的時候說明上一個正則函數出錯, 特別的對於文章的例子, 出錯返回(PREG_BACKTRACK_LIMIT_ERROR)
2、非貪婪模式導致太多回溯, 必然會有一些效能問題, 適當的該寫下正則, 是可以避免這個問題的. 尤其在做大資料量的文本處理的時候, 如果正則設計不慎, 很容易導致深度嵌套, 另外考慮到效能, 還是建議能用字串處理盡量使用字串處理代替.