PHP 正則處理中文需考慮編碼(UTF-8/GBK/BIG5)、PCRE 版本、多位元組字元邊界。本文系統整理各編碼下的匹配模式、常見陷阱與現代最佳實踐(Unicode 屬性)。
使用 \p{Han} 匹配所有漢字(CJK Unified Ideographs),\p{Han}+ 匹配連續漢字串。必須加 u 修飾符 啟用 UTF-8 模式。
$str = 'Hello 世界 123 测试'; // 1. 判斷是否包含中文 if (preg_match('/\p{Han}/u', $str)) { echo '包含中文'; } // 2. 判斷純中文字串(全字串皆為漢字) if (preg_match('/^\p{Han}+$/u', $str)) { echo '純中文'; } else { echo '含非中文'; } // 3. 提取所有中文詞彙 preg_match_all('/\p{Han}+/u', $str, $matches); print_r($matches[0]); // ['世界', '测试'] // 4. 替換中文為標記 echo preg_replace('/\p{Han}+/u', '[中文]', $str); // Hello [中文] 123 [中文]
| 屬性 | 涵蓋範圍 | 說明 |
|---|---|---|
\p{Han} | CJK Unified Ideographs | 通用漢字(簡繁體、日韓漢字) |
\p{Hiragana} | 平假名 | 日文平假名 |
\p{Katakana} | 片假名 | 日文片假名 |
\p{Hangul} | 韓文音節 | 韓文 |
\p{L} | 所有字母 | 含拉丁、希臘、西里爾、漢字等 |
\p{N} | 所有數字 | 含阿拉伯數字、中文數字、羅馬數字 |
\p{P} | 所有標點 | 含中文標點、英文標點 |
若專案仍使用 GBK/GB2312 編碼,無法使用 u 修飾符,需用位元組範圍匹配:
// GBK/GB2312:雙位元組,高位 0x81-0xFE,低位 0x40-0xFE (排除 0x7F) $gbk_han = '/[\x81-\xFE][\x40-\xFE]/'; // BIG5:高位 0x81-0xFE,低位 0x40-0x7E 及 0xA1-0xFE $big5_han = '/[\x81-\xFE][\x40-\x7E\xA1-\xFE]/'; // 判斷純中文(GBK 範例) $str_gbk = '中文測試'; // 假設檔案儲存為 GBK if (preg_match('/^(' . $gbk_han . ')+$/', $str_gbk)) { echo 'GBK 純中文'; } // 注意:若 PHP 檔案為 UTF-8 但字串為 GBK,需先 iconv 轉碼再用 \p{Han}
// 設定內部編碼(腳本頂端呼叫一次) mb_regex_encoding('UTF-8'); // 或 'GBK', 'BIG5' $str = '中文測試123'; // mb_eregi:不分大小寫匹配(中文無大小寫,等同 mb_ereg) if (mb_ereg('\p{Han}', $str)) { // PHP 5.4+ mb_ereg 支援 Unicode 屬性 echo '含中文'; } // 純中文判斷 if (mb_ereg('^\p{Han}+$', $str)) { echo '純中文'; } // 分割中英文 $parts = mb_split('(\p{Han}+)', $str); // 保留分隔符需用 mb_ereg 迴圈
| 需求 | 模式(UTF-8) | 說明 |
|---|---|---|
| 用戶名:中英數底線,2-20 字 | /^[\p{Han}\w]{2,20}$/u | \w = [a-zA-Z0-9_] |
| 密碼:含至少一個中文 | /^(?=.*\p{Han}).{8,}$/u | 正向預查 |
| 過濾 Emoji(輔助平面) | /[\x{10000}-\x{10FFFF}]/u | 或 /[\p{So}\p{Cn}]/u |
| 提取中文句子(含中文標點) | /[\p{Han}\p{P}]+/u | \p{P} 含所有標點 |
| 驗證身分證字號(台) | /^[A-Z][12]\d{8}$/ | 無中文,僅示例 |
| 驗證統一編號(台) | /^\d{8}$/ | 同上 |
u 修飾符:否則 PCRE 將字串視為單位元組,導致多位元組字元被拆解匹配,產生錯誤結果。preg_match_all 搭配複雜回溯模式:如 /(\p{Han}+)+/u 會導致災難性回溯,改用 /\p{Han}+/u 並程式端處理。ini_set('pcre.backtrack_limit', '1000000') 可提高上限,但優先優化模式。preg_* 可能受影響,建議關閉並顯式使用 mb_* 函數。class ChineseValidator { public static function hasChinese(string $str): bool { return (bool)preg_match('/\p{Han}/u', $str); } public static function isPureChinese(string $str): bool { return (bool)preg_match('/^\p{Han}+$/u', $str); } public static function extractChinese(string $str): array { preg_match_all('/\p{Han}+/u', $str, $m); return $m[0]; } public static function filterChinese(string $str, string $replacement = ''): string { return preg_replace('/\p{Han}+/u', $replacement, $str); } public static function usernameValid(string $username): bool { // 2-20 字,中英數底線,至少一個中文或英文 return (bool)preg_match('/^(?=.*[\p{Han}a-zA-Z])[\p{Han}\w]{2,20}$/u', $username); } }