正則表達式的捕獲組是擷取、替換、驗證字串的核心機制。PHP 採用 PCRE 庫,完整支援編號捕獲組、命名捕獲組、非捕獲組、反向引用、前瞻後顧等進階功能。本文系統性講解各類捕獲組語法、行為差異與實戰應用。
$pattern = '/a=(\d+)b=(\d+)c=(\d+)/';
$str = 'a=4b=98c=56';
preg_match($pattern, $str, $matches);
print_r($matches);
/*
Array
(
[0] => a=4b=98c=56 // 完整匹配(編號 0)
[1] => 4 // 第 1 個捕獲組
[2] => 98 // 第 2 個捕獲組
[3] => 56 // 第 3 個捕獲組
)
*/
編號規則:由左至右按左括號 ( 出現順序編號,從 1 開始。巢狀括號同樣遵循此規則。
$pattern = '/a=(\d+)b=(?:\d+)c=(\d+)/'; // 第 2 組不捕獲
preg_match($pattern, 'a=4b=98c=56', $matches);
/*
Array
(
[0] => a=4b=98c=56
[1] => 4
[2] => 56 // 原第 3 組變為第 2 組
)
*/
用途:分組套用量詞(如 (?:abc)+)、邏輯分組但不佔用編號、提升效能(略過捕獲儲存)。
$pattern = '/a=(\d+)b=(?\d+)c=(\d+)/'; preg_match($pattern, 'a=4b=98c=56', $matches); /* Array ( [0] => a=4b=98c=56 [1] => 4 [sec] => 98 // 命名鍵值 [2] => 98 // 同時保留編號 [3] => 56 ) */
優點:程式碼可讀性高、不受組別順序變動影響、支援 $matches['name'] 直接存取。
// 匹配重複單詞
$pattern = '/\b(\w+)\s+\1\b/';
preg_match($pattern, 'hello hello world'); // 匹配 "hello hello"
// 替換時使用反向引用
$pattern = '/(\d{4})-(\d{2})-(\d{2})/';
$replacement = '$3/$2/$1'; // 或 \\3/\\2/\\1
echo preg_replace($pattern, $replacement, '2024-01-15'); // 15/01/2024
// 命名反向引用(PHP 7+)
$pattern = '/(?\d{4})-(?\d{2})-(?\d{2})/';
$replacement = '${day}/${month}/${year}';
| 語法 | 名稱 | 含義 | 範例匹配 |
|---|---|---|---|
| (?=...) | 正向前瞻 | 後面必須是... | \d(?=px) 匹配 "12px" 中的 "12" |
| (?!...) | 負向前瞻 | 後面不能是... | \d(?!px) 匹配 "12em" 中的 "12" |
| (?<=...) | 正向後顧 | 前面必須是... | (?<=\$)\d+ 匹配 "$100" 中的 "100" |
| (? | 負向後顧 | 前面不能是... | (? |
// 正向前瞻:匹配後面跟著 abc 的數字
preg_match('/\d(?=abc)/', 'ab36abc8eg', $m); // $m[0] = "6"
// 正向後顧:匹配前面是 abc 的數字
preg_match('/(?<=abc)\d/', 'ab36abc8eg', $m); // $m[0] = "8"
// 實戰:千分位分隔符(不匹配小數點後)
$number = '1234567.89';
echo preg_replace('/(?<=\d)(?=(\d{3})+(?!\d))/', ',', $number); // 1,234,567.89
$url = 'https://example.com/path?id=123&name=test&tag=php%20regex'; $pattern = '/[?&](?[^=&]+)=(? [^&]*)/'; preg_match_all($pattern, $url, $matches, PREG_SET_ORDER); foreach ($matches as $match) { echo "{$match['key']} = " . urldecode($match['value']) . "\n"; } /* id = 123 name = test tag = php regex */
(?:...) 除非需要捕獲內容^.* 開頭preg_match_all() 批次處理優於迴圈 preg_match()深入掌握捕獲組機制,能編寫高效、可維護的正則表達式,解決複雜字串解析與轉換需求。