處理跨系統資料交換 (如 API 串接、表單提交、檔案匯入) 時,常遇到編碼不一導致亂碼。本文提供一個輕量級函數 safeEncoding(),自動偵測字串為 UTF-8 或 GB2312/GBK,並統一轉換為 UTF-8 (或指定目標編碼)。
<?php
/**
* 自動偵測字串編碼並轉換為目標編碼 (預設 UTF-8)
*
* @param string $string 待處理字串
* @param string $outEncoding 目標編碼,預設 'UTF-8'
* @return string 轉換後字串
*/
function safeEncoding($string, $outEncoding = 'UTF-8') {
$encoding = 'UTF-8'; // 預設假設為 UTF-8
$len = strlen($string);
for ($i = 0; $i < $len; $i++) {
$char = $string[$i];
$ord = ord($char);
// ASCII 範圍 (0-127) 直接跳過,所有編碼相容
if ($ord < 128) {
continue;
}
// 檢測 UTF-8 三位元組模式: 1110xxxx 10xxxxxx 10xxxxxx
if (($ord & 0xE0) === 0xE0) { // 224 = 11100000
// 確保還有至少 2 個位元組
if ($i + 2 < $len) {
$char1 = $string[$i + 1];
$char2 = $string[$i + 2];
if (($char1 & 0x80) === 0x80 && ($char2 & 0x80) === 0x80) {
$encoding = 'UTF-8';
break; // 確認為 UTF-8,停止檢測
}
}
}
// 檢測 GB2312/GBK 雙位元組模式: 110xxxxx 10xxxxxx (首位元組 0x81-0xFE, 次位元組 0x40-0xFE 除 0x7F)
if (($ord & 0xC0) === 0xC0) { // 192 = 11000000
if ($i + 1 < $len) {
$char1 = $string[$i + 1];
if (($char1 & 0x80) === 0x80) {
$encoding = 'GB2312';
break; // 確認為 GB 系列,停止檢測
}
}
}
}
// 已是目標編碼直接返回
if (strtoupper($encoding) === strtoupper($outEncoding)) {
return $string;
}
// 使用 iconv 轉碼,//IGNORE 忽略無法轉換字元,//TRANSLIT 嘗試音譯
return iconv($encoding, $outEncoding . '//IGNORE', $string);
}
?>
<?php
// 模擬 GB2312 編碼字串 (實際環境可能來自 $_POST、檔案讀取、API 回傳)
$gbString = iconv('UTF-8', 'GB2312', '測試中文內容');
echo '原始編碼偵測: ' . mb_detect_encoding($gbString) . PHP_EOL; // 可能偵測不準
$utf8String = safeEncoding($gbString);
echo '轉碼後: ' . $utf8String . PHP_EOL;
echo '轉碼後編碼: ' . mb_detect_encoding($utf8String) . PHP_EOL;
// 指定輸出為 GBK (較少見需求)
$gbkString = safeEncoding($utf8String, 'GBK');
?>
1110xxxx (0xE0-0xEF),後續兩位元組皆為 10xxxxxx (0x80-0xBF)。110xxxxx (0x81-0xFE),次位元組 10xxxxxx (0x40-0xFE, ≠0x7F)。| 場景 | 適用性 | 說明 |
|---|---|---|
| 表單提交 (舊系統 GB2312 → 新系統 UTF-8) | ★★★★★ | 最常見用途,自動修正亂碼 |
| API 串接 (不確定對方編碼) | ★★★★☆ | 配合 Content-Type 標頭更準確 |
| CSV/Excel 檔案匯入 | ★★★★☆ | 建議先用 mb_detect_encoding 輔助 |
| 純英文/數字/符號字串 | ★★★★★ | ASCII 範圍直接返回,零開銷 |
| Big5、Shift_JIS、EUC-KR 等其他編碼 | ★☆☆☆☆ | 本函數僅區分 UTF-8/GB 系列,其他編碼會誤判 |
| 極短字串 (如單字「中」) | ★★★☆☆ | 特徵碼不足可能誤判,建議加長度判斷 |
Content-Type: charset=... 或 ``, 直接用該編碼,不需偵測。EF BB BF),有 BOM 確定為 UTF-8。mbstring 擴充並使用 mb_detect_encoding($str, ['UTF-8', 'GB2312', 'GBK', 'ASCII'], true),官方實作更完善。SET NAMES utf8mb4,從源頭避免編碼問題。