preg_match() 是 PHP 執行 **PCRE (Perl 相容正則表達式)** 匹配的核心函數,僅返回首次匹配結果。本文系統整理語法、參數、修飾符、常用模式、效能優化與實戰陷阱。
preg_match(
string $pattern, // 正則模式 (含定界符)
string $subject, // 目標字串
array &$matches = null, // 匹配結果引用
int $flags = 0, // 標記
int $offset = 0 // 起始偏移量
): int|false
1:匹配成功0:匹配失敗false:錯誤 (如模式編譯失敗)$str = 'Email: test@example.com, Phone: 138-0000-1111';
preg_match('/([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,})/i', $str, $m);
print_r($m);
/*
Array
(
[0] => test@example.com // 整個模式匹配內容
[1] => test@example.com // 第 1 個捕獲組
)
*/
$matches[0]:完整匹配$matches[1]...$matches[n]:第 n 個捕獲組 (...)(?P...) 同時生成數字鍵與字串鍵| 修飾符 | 名稱 | 效果 |
|---|---|---|
i | PCRE_CASELESS | 大小寫不敏感 |
m | PCRE_MULTILINE | ^$ 匹配行首尾而非字串首尾 |
s | PCRE_DOTALL | . 匹配換行符 |
u | PCRE_UTF8 | **UTF-8 模式 (中文必加)**,正確處理多位元組字元 |
x | PCRE_EXTENDED | 忽略模式空白/註釋,可寫多行可讀模式 |
A | PCRE_ANCHORED | 強制從字串開頭匹配 (等價 ^) |
D | PCRE_DOLLAR_ENDONLY | $ 僅匹配字串結尾,不匹配末尾換行前 |
U | PCRE_UNGREEDY | 反轉貪婪/非貪婪量詞預設行為 |
J | PCRE_DUPNAMES | 允許重複命名捕獲組 |
$text = '訪問 https://example.com/path?query=1 或 https://test.org';
preg_match('/https?:\/\/[^\s<>"\']+/i', $text, $m);
// $m[0] = 'https://example.com/path?query=1'
function isMobile(string $phone): bool {
return preg_match('/^1[3-9]\d{9}$/', $phone) === 1;
}
$html = ''; preg_match('/
]*src\s*=\s*["\']([^"\']+)["\']/i', $html, $m); // $m[1] = 'avatar.jpg'
$dateStr = '出生日期:1990-05-12';
preg_match('/(\d{4})-(\d{2})-(\d{2})/', $dateStr, $m);
// $m = ['1990-05-12', '1990', '05', '12']
$log = 'user=john action=login ip=192.168.1.1';
preg_match('/(?P\w+)\s+(?P\w+)\s+(?P[\d.]+)/', $log, $m);
/*
$m = [
0 => 'user=john action=login ip=192.168.1.1',
'user' => 'john', 1 => 'john',
'action' => 'login', 2 => 'login',
'ip' => '192.168.1.1', 3 => '192.168.1.1'
]
*/
$text = '你好世界 Hello';
preg_match('/[\p{Han}]+/u', $text, $m); // \p{Han} 匹配漢字
// $m[0] = '你好世界'
preg_match('/^\p{L}+$/u', '你好'); // 純字母(含中文) -> 1
preg_match('/^\w+$/', '你好'); // 無 u 修飾符 -> 0 (\w 不匹配中文)
| 常數 | 說明 |
|---|---|
PREG_OFFSET_CAPTURE | 結果包含偏移量 [匹配內容, 偏移量] |
PREG_UNMATCHED_AS_NULL | 未匹配的捕獲組設為 null 而非空字串 (PHP 7.2+) |
preg_match('/(\d+)-(\d+)/', 'abc-123', $m, PREG_OFFSET_CAPTURE);
/*
$m = [
0 => ['123', 4],
1 => ['123', 4],
2 => [null, -1] // 第二組未匹配
]
*/
^pattern 或 pattern$ 減少回溯[a-z] 優於 .,\d 優於 [0-9](a+)* 導致災難性回溯(?:...) 不需捕獲時節省記憶體strpos/substr/explode,快 10-100 倍| 陷阱 | 錯誤代碼 | 正確代碼 | 原因 |
|---|---|---|---|
| 定界符缺失 | preg_match('/pattern', $s) | preg_match('/pattern/', $s) | 模式必須被定界符包圍 |
| 定界符衝突 | preg_match('/https:///', $s) | preg_match('~https://~', $s) | 改用 ~ # @ 等不衝突定界符 |
| 中文亂碼/匹配失敗 | preg_match('/\w+/', '中文') | preg_match('/\w+/u', '中文') | **必須加 u 修飾符** 啟用 UTF-8 |
| 貪婪匹配過度 | preg_match('/ .*<\/div>/', $html) | preg_match('/ .*?<\/div>/', $html) | 改用非貪婪 *? 或 [^<]* |
| 轉義遺漏 | preg_match('/file.php/', $s) | preg_match('/file\.php/', $s) | . 需轉義為 \. |
| 位元組偏移 vs 字元偏移 | $offset 按字元計算 | 配合 u 修飾符,offset 為字元位置 | 無 u 時 offset 為位元組 |
| 函數 | 用途 | 返回 | 典型場景 |
|---|---|---|---|
preg_match | 首次匹配 | 0/1/false | 驗證、提取第一個、條件判斷 |
preg_match_all | 全局匹配 | 匹配次數 | 提取所有郵箱、分詞、日誌解析 |
preg_replace | 搜尋替換 | 新字串/次數 | 關鍵字高亮、模板渲染、清洗 |
preg_replace_callback | 回調替換 | 新字串 | 複雜替換邏輯 (如 BBCode 解析) |
preg_split | 正則分割 | 陣列 | 分割複雜分隔符、保留分隔符 |
preg_quote | 轉義正則元字元 | 字串 | 動態構建模式時保護用戶輸入 |
// php.ini
pcre.jit=1 ; 預設開啟
// 運行時檢查
var_dump(pcre_jit_support()); // bool(true)
// 複雜模式受益最大,簡單模式 JIT 開銷可能超過收益
// 可通過 ini_set('pcre.jit', '0') 關閉
掌握 preg_match 核心在於:**正確的模式構建 + u 修飾符處理 UTF-8 + 理解貪婪/非貪婪 + 善用命名捕獲組**。複雜解析任務優先考慮專用解析器 (HTML 用 DOMDocument、JSON 用 json_decode、URL 用 parse_url),正則作為輔助工具而非萬能鑰匙。