PHP 判斷字串是否包含中文,關鍵在於理解編碼與正則表達式的 Unicode 屬性支援。本文詳細比較 GBK/UTF-8 環境下的判斷方法,提供現代最佳實踐。
| 編碼 | 中文範圍 | 英文/數字 | PHP 內部處理 |
|---|---|---|---|
| GBK/GB2312 | 0x8140-0xFEFE (雙位元組) | 0x00-0x7F (單位元組) | 不支援,需 iconv/mbstring |
| UTF-8 | U+4E00-U+9FFF (CJK 統一表意文字) | U+0000-U+007F (ASCII) | 原生支援 (PHP 7+) |
<?php
function hasChinese(string $str): bool {
// \p{Han} 匹配所有漢字(CJK 統一表意文字)
return (bool)preg_match('/\p{Han}/u', $str);
}
function isAllChinese(string $str): bool {
// 全字串皆為漢字
return (bool)preg_match('/^\p{Han}+$/u', $str);
}
function hasEnglish(string $str): bool {
// \p{Latin} 或 ASCII 範圍
return (bool)preg_match('/[A-Za-z]/', $str);
}
function isAllEnglish(string $str): bool {
return (bool)preg_match('/^[A-Za-z0-9\s]+$/', $str);
}
// 測試
var_dump(hasChinese('Hello 世界')); // true
var_dump(isAllChinese('你好世界')); // true
var_dump(hasChinese('Hello World')); // false
var_dump(isAllEnglish('Hello World')); // true
?>
<?php
// UTF-8 中文範圍:\x{E4}\x{B8}\x{80} - \x{E9}\x{BE}\x{8F} (U+4E00-U+9FFF)
function hasChineseLegacy(string $str): bool {
return (bool)preg_match('/[\x{4e00}-\x{9fff}]/u', $str);
}
// 更完整範圍(含擴展區)
function hasChineseFull(string $str): bool {
return (bool)preg_match(
'/[\x{3400}-\x{4dbf}\x{4e00}-\x{9fff}\x{f900}-\x{faff}\x{20000}-\x{2a6df}\x{2a700}-\x{2b73f}\x{2b740}-\x{2b81f}\x{2b820}-\x{2ceaf}]/u',
$str
);
}
?>
<?php
function hasChineseGbk(string $str): bool {
// GBK 中文雙位元組高位元組 0x81-0xFE,低位元組 0x40-0xFE (排除 0x7F)
// 簡化檢測:檢查是否有高位元組 (>= 0x80)
return (bool)preg_match('/[\x80-\xFF]/', $str);
}
// 較精確(需 iconv 轉碼後判斷)
function hasChineseGbkAccurate(string $str): bool {
$utf8 = @iconv('GBK', 'UTF-8//IGNORE', $str);
return $utf8 !== false && hasChinese($utf8);
}
?>
<?php
// 逐字符檢查(效能較差但直觀)
function hasChineseMb(string $str): bool {
$len = mb_strlen($str, 'UTF-8');
for ($i = 0; $i < $len; $i++) {
$char = mb_substr($str, $i, 1, 'UTF-8');
$code = mb_ord($char, 'UTF-8');
if ($code >= 0x4E00 && $code <= 0x9FFF) return true;
}
return false;
}
// 統計中英文字符數
function countCharTypes(string $str): array {
$chinese = $english = $digit = $other = 0;
$len = mb_strlen($str, 'UTF-8');
for ($i = 0; $i < $len; $i++) {
$char = mb_substr($str, $i, 1, 'UTF-8');
$code = mb_ord($char, 'UTF-8');
if ($code >= 0x4E00 && $code <= 0x9FFF) $chinese++;
elseif ($code >= 0x41 && $code <= 0x5A) $english++; // A-Z
elseif ($code >= 0x61 && $code <= 0x7A) $english++; // a-z
elseif ($code >= 0x30 && $code <= 0x39) $digit++;
else $other++;
}
return compact('chinese', 'english', 'digit', 'other');
}
?>
| 方法 | 效能 | 準確度 | 相依性 | 推薦場景 |
|---|---|---|---|---|
| preg_match \p{Han} | 快 | 高 (Unicode 標準) | PCRE 8.1+ | 現代專案首選 |
| preg_match 範圍 | 快 | 中 (需維護範圍) | PCRE + u 修飾符 | 舊版 PHP 相容 |
| mbstring 逐字 | 慢 | 最高 (可自訂邏輯) | mbstring 擴展 | 需統計/複雜判斷 |
| GBK 高位元組 | 最快 | 低 (誤判日文/符號) | 無 | 極舊專案維護 |
<?php
function validateUsername(string $username): array {
$errors = [];
if (mb_strlen($username, 'UTF-8') < 2) {
$errors[] = '長度至少 2 字符';
}
if (mb_strlen($username, 'UTF-8') > 20) {
$errors[] = '長度不超過 20 字符';
}
if (!preg_match('/^[\p{Han}\p{Latin}\p{N}_]+$/u', $username)) {
$errors[] = '僅允許中文、英文、數字、底線';
}
if (preg_match('/^\d+$/', $username)) {
$errors[] = '不能純數字';
}
return ['valid' => empty($errors), 'errors' => $errors];
}
?>
核心原則:專案統一使用 UTF-8,配合 /\p{Han}/u 正則可解決 99% 中文判斷需求,避免編碼轉換帶來的錯誤與效能損耗。