正則表達式是字串處理的利器,但錯誤的量詞選擇可能導致嚴重效能問題,甚至造成堆疊溢位。本文深入剖析貪婪/非貪婪匹配機制、回溯原理,並透過實例展示如何撰寫高效能的正則模式。
量詞後加 ? 即為非貪婪(惰性)模式:
*、+、?、{n,m} —— 盡可能多匹配*?、+?、??、{n,m}? —— 盡可能少匹配$str = '<script<script>alert(document.cookie)</script>>alert(document.cookie)</script>';
// 非貪婪模式
$str = preg_replace('%<script>.+?</script>%i', '', $str);
// 結果:<script>alert(document.cookie)</script> (殘留外層標籤)
// 貪婪模式
$str = preg_replace('%<script>.+</script>%i', '', $str);
// 結果:<script (過度匹配至最後一個閉合標籤)
正確寫法應排除中間出現的閉合標籤:
%<script>(?:(?!<\/script>).)+<\/script>%
模式:\w*(\d+),字串:cfc456n
\w* 先佔據整個字串 cfc456n\d+ 無字元可匹配,觸發回溯\w* 逐字吐出:n→6,\d+ 匹配到 6 成功$1 = "6"(非預期的 456)模式:\w*?(\d+),字串:cfc456n
\w*? 從 0 字元開始嘗試,每次匹配 1 字元後交給 \d+c、f、c 失敗後,\d+ 終於匹配到 456$1 = "456",但執行了大量回溯步驟$reg = "/<script>.*?<\/script>/is";
$str = "<script>" . str_repeat("*", 100014) . "</script>";
$ret = preg_replace($reg, "", $str); // 返回 NULL,堆疊耗盡
解決方案:使用佔有優先量詞或原子分組避免不必要回溯,或改用 DOM 解析器處理 HTML。
[^<]*)替代 .*?(a*)*)preg_match_all 分段處理