萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> PHP 程式設計

PHP 正則表達式效能深度解析:貪婪、非貪婪與回溯機制

前言

正則表達式是字串處理的利器,但錯誤的量詞選擇可能導致嚴重效能問題,甚至造成堆疊溢位。本文深入剖析貪婪/非貪婪匹配機制、回溯原理,並透過實例展示如何撰寫高效能的正則模式。

貪婪 vs 非貪婪量詞

量詞後加 ? 即為非貪婪(惰性)模式:

  • 貪婪:*、+、?、{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>%

回溯機制圖解

案例 1:貪婪導致的回溯

模式:\w*(\d+),字串:cfc456n

  1. \w* 先佔據整個字串 cfc456n
  2. \d+ 無字元可匹配,觸發回溯
  3. \w* 逐字吐出:n→6,\d+ 匹配到 6 成功
  4. 結果 $1 = "6"(非預期的 456)

案例 2:非貪婪的逐步嘗試

模式:\w*?(\d+),字串:cfc456n

  1. \w*? 從 0 字元開始嘗試,每次匹配 1 字元後交給 \d+
  2. 歷經 c、f、c 失敗後,\d+ 終於匹配到 456
  3. 結果 $1 = "456",但執行了大量回溯步驟

效能殺手:過度回溯導致堆疊溢位

$reg = "/<script>.*?<\/script>/is";
$str = "<script>" . str_repeat("*", 100014) . "</script>";
$ret = preg_replace($reg, "", $str); // 返回 NULL,堆疊耗盡

解決方案:使用佔有優先量詞或原子分組避免不必要回溯,或改用 DOM 解析器處理 HTML。

最佳實踐建議

  • 優先使用具體字元類別(如 [^<]*)替代 .*?
  • 避免巢狀量詞(如 (a*)*)
  • 長字串處理考慮 preg_match_all 分段處理
  • 複雜 HTML/XML 處理請用專門解析器(DOMDocument、SimpleXML)
copyright © 萬盛學電腦網 all rights reserved