<?php
$str = "測試字符串";
$encoding = mb_detect_encoding($str, ['UTF-8', 'GB2312', 'GBK', 'BIG5'], true);
echo $encoding; // UTF-8
// 嚴格模式:無法確定時返回 false
$encoding = mb_detect_encoding($str, 'UTF-8', true);
前提:php.ini 啟用 extension=mbstring。
function is_utf8($string) {
return (bool)preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE-\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs', $string);
}
function is_gb2312($str) {
for ($i = 0; $i < strlen($str); $i++) {
$v = ord($str[$i]);
if ($v > 127) { // 高位字節
if ($v >= 228 && $v <= 233) { // UTF-8 三字節首字節範圍
if ($i + 2 >= strlen($str)) return true; // 長度不足視為 GBK
$v1 = ord($str[$i+1]);
$v2 = ord($str[$i+2]);
if ($v1 >= 128 && $v1 <= 191 && $v2 >= 128 && $v2 <= 191) {
return false; // 符合 UTF-8 續字節特徵
}
}
return true; // 其他高位字節視為 GBK
}
}
return true; // 純 ASCII 兼容視為 GBK
}
function to_utf8($str) {
if (is_utf8($str)) return $str;
// 嘗試常見中文編碼
foreach (['GBK', 'GB2312', 'BIG5'] as $enc) {
$converted = @iconv($enc, 'UTF-8//IGNORE', $str);
if ($converted !== false && is_utf8($converted)) {
return $converted;
}
}
return $str; // 實在無法識別則原樣返回
}
<?php
// 1. 檢測編碼
$encoding = mb_detect_encoding($input, ['UTF-8', 'GBK', 'GB2312', 'BIG5'], true);
// 2. 統一轉為 UTF-8 內部處理
$utf8 = mb_convert_encoding($input, 'UTF-8', $encoding);
// 3. 業務邏輯處理...
// 4. 輸出時按需轉碼
$output = mb_convert_encoding($utf8, 'GBK', 'UTF-8'); // 給老系統
// 或
header('Content-Type: text/html; charset=UTF-8');
echo $utf8; // 現代 Web 直接輸出 UTF-8
| 現象 | 原因 | 解決 |
|---|---|---|
| 亂碼 | UTF-8 數據被當作 GBK 讀取 | 確保 HTTP Header、HTML meta、PHP 內部、MySQL 連接全鏈路 UTF-8 |
| ? 佔位符 | GBK 數據強行按 UTF-8 解碼,非法字節被替換 | 正確指定源編碼 iconv('GBK', 'UTF-8//IGNORE', $str) |
| iconv 返回 false | 源編碼判斷錯誤或包含非法字節 | 用 //IGNORE 或 //TRANSLIT 忽略/轉義非法字節 |
| 中文截斷亂碼 | 按字節截取多字節字符 | 用 mb_substr($str, 0, 10, 'UTF-8') |
to_utf8()。charset=utf8mb4` / mysqli `set_charset('utf8mb4')`。iconv('UTF-8', 'UTF-8', ...)。mb_detect_encoding 較慢(需嘗試多種編碼),高頻調用處緩存結果或預先約定編碼。iconv 比 mb_convert_encoding 略快,大批量轉碼優先用 iconv。!preg_match('/[\\x80-\\xFF]/', $str) 極快排除。