正則表達式是文本處理的瑞士軍刀。PHP preg_replace() 作為 PCRE 庫的核心接口,功能強大但細節眾多。本文系統梳理語法規則、定界符、修飾符、回調替換、向後引用,並給出生產級實戰模式。
preg_replace(
mixed $pattern, // 正則模式 (字符串或陣列)
mixed $replacement, // 替換內容 (字符串、陣列、可調用對象)
mixed $subject, // 目標字符串 (或陣列)
int $limit = -1, // 最大替換次數 (-1 無限制)
int &$count = null // 實際替換次數引用返回
): mixed
關鍵差異:JS String.replace 預設只替換首次匹配 (需 /g 全局),PHP preg_replace 預設全局替換所有匹配,$limit 控制上限。
PCRE 要求模式被定界符包裹。常用 /、#、~、%。
$url = 'https://example.com/path/';
// 方案 A: / 定界符,需轉義內部 /
preg_replace('/http:\/\/example\.com\//', 'https://new.com/', $url);
// 方案 B: # 定界符,無需轉義 / (推薦含路徑 URL)
preg_replace('#https://example\.com/#', 'https://new.com/', $url);
// 方案 C: 變量插值雙引號 + 定界符
$pattern = "#^$prefix#"; // 雙引號解析變量
最佳實踐:模式含 / (路徑、HTML 標籤) 用 #;含 # 用 ~;通用情況堅持 / 形成肌肉記憶 (JS/Python/Go 同用 /)。
| 修飾符 | 作用 | 備註 |
|---|---|---|
i | 忽略大小寫 | 最常用 |
m | 多行模式:^/$ 匹配行首尾 | 配合 \n 字符串 |
s | . 匹配換行符 \n | 單行模式 (DOTALL) |
x | 忽略模式中未轉義空白,可寫註釋 | 複雜正則可讀性 |
e | 已棄用/移除 (PHP 7.0+) preg_replace_callback 替代 | 安全風險極高 |
A | 強制從字符串開頭匹配 | 等價 ^ 但更快 |
D | $ 僅匹配字符串絕對末尾 | 忽略末尾 \n,與 m 衝突 |
S | 分析非錨定模式優化 (研究用) | 罕用 |
U | 反貪婪:量詞預設非貪婪,? 恢復貪婪 | 調試用,不推薦生產 |
X | 嚴格模式:非標準轉義報錯 | 開發階段建議開啟 |
u | UTF-8 模式:正確處理多字節字符 | 中文必加,否則按字節匹配 |
J | 允許重複命名子組 (?<name>...) | PHP 7.0+ |
$str = 'W3CSchool 在線教程';
// 錯誤:無 u 修飾符,中文按字節拆分匹配失敗
preg_replace('/在線/', 'Online', $str); // 可能不生效
// 正確:強制 UTF-8
preg_replace('/在線/u', 'Online', $str); // W3CSchool Online教程
// 忽略大小寫 + UTF-8
preg_replace('/w3cschool/iu', 'W3C', $str);
$text = "Line1\nLine2\nLine3";
// 無 s 修飾符:. 不匹配 \n
preg_replace('/^Line.*Line$/m', '[BLOCK]', $text);
// m: ^$ 匹配每行,但 . 不跨行,結果無替換
// 加 s:. 匹配 \n,整段視為單行
preg_replace('/^Line.*Line$/s', '[BLOCK]', $text);
// 結果: [BLOCK]
// 多行替換每行
preg_replace('/^/m', '> ', $text); // > Line1\n> Line2\n> Line3
<?php
$html = '<a href="HTTP://EXAMPLE.COM">Link</a>';
// 將 URL 標準化為小寫
$html = preg_replace_callback(
'#href="([^"]+)"#i',
function ($matches) {
return 'href="' . strtolower($matches[1]) . '"';
},
$html
);
// 輸出: Link
// 靜態方法回調 (PHP 5.3+)
class Sanitizer {
public static function lowerUrl($m) { return 'href="' . strtolower($m[1]) . '"'; }
}
preg_replace_callback('#href="([^"]+)"#i', ['Sanitizer', 'lowerUrl'], $html);
| 語法 | 說明 | 示例 |
|---|---|---|
$1 $2... | PHP 雙引號字符串中推薦 (單引號不解析) | preg_replace('/(\d+)-(\d+)/', '$2-$1', '123-456') → 456-123 |
\1 \2... | 單引號字符串或 JS 風格 | preg_replace('/(\d+)-(\d+)/', '\\2-\\1', '123-456') |
${1} ${name} | 消除歧義 (如 $11 是第 11 組還是第 1 組+字符 1) | preg_replace('/(?<num>\d+)/', '${num}0', '5') → 50 |
$pattern = '/^((\w+)\s+(\d+))$/'; // 外層組1,內層組2、3
$replacement = 'Full:$1 Word:$2 Num:$3';
preg_replace($pattern, $replacement, 'Hello 123');
// Full:Hello 123 Word:Hello Num:123
| 場景 | 模式 | 替換 | 說明 |
|---|---|---|---|
| XSS 過濾 script 標籤 | #<script[^>]*>.*?</script>#is | '' | s 跨行,i 忽略大小寫 |
| 提取 img src | #<img[^>]+src=["']([^"']+)["']#i | 回調收集 $1 | 非貪婪屬性匹配 |
| 手機號脫敏 | #(\d{3})\d{4}(\d{4})# | '$1****$2' | 捕獲首尾 4 位 |
| Markdown 鏈接轉 HTML | #\[([^\]]+)\]\(([^)]+)\)# | '<a href="$2">$1</a>' | 命名組更清晰 |
| 去除多餘空白行 | #\n\s*\n# | "\n\n" | 保留單空行 |
str_replace/strtr 絕不用正則 (固定字符串替換快 10-100 倍)。(.*)*、(a+)+ 等嵌套量詞在長字符串導致指數級回溯,改用 (?>...) 原子組或具體字符類。^、\A、$、\z 讓引擎快速失敗/成功。正則強大但雙刃劍。掌握 PHP preg_replace 完整語義、修飾符交互、回調替換、向後引用細節,配合性能鐵律,才能寫出高效、可維護、安全的文本處理代碼。