處理用戶上傳文件、第三方 API 數據、爬蟲內容時,常面臨「這文本到底是什麼編碼?」的問題。PHP 沒有內置「檢測編碼」函數(mb_detect_encoding 準確率一般),但可以利用 iconv 往返轉換無損 的特性來驗證。
若字符串 $str 真的是編碼 $encoding,則:
$str === iconv('UTF-8', $encoding, iconv($encoding, 'UTF-8', $str))
即:原編碼 → UTF-8 → 原編碼 往返後內容不變。若編碼錯誤,iconv 會產生替換字符或截斷,導致不等。
/**
* 檢測字符串編碼
*
* @param string $string 待檢測字符串
* @param array $candidates 候選編碼列表,按優先級排序
* @return string|null 匹配的編碼或 null
*/
function detectEncoding(string $string, array $candidates = ['UTF-8', 'GBK', 'GB2312', 'BIG5', 'ISO-8859-1']): ?string
{
foreach ($candidates as $encoding) {
// 先嘗試轉 UTF-8,再轉回原編碼
$converted = @iconv($encoding, 'UTF-8', $string);
if ($converted === false) continue; // 轉換失敗
$back = @iconv('UTF-8', $encoding, $converted);
if ($back === false) continue;
// 忽略 BOM 差異
$cleanOriginal = ltrim($string, "\xEF\xBB\xBF");
$cleanBack = ltrim($back, "\xEF\xBB\xBF");
if ($cleanOriginal === $cleanBack) {
return $encoding;
}
}
return null;
}
$tests = [
'UTF-8' => '你好世界',
'GBK' => iconv('UTF-8', 'GBK', '你好世界'),
'BIG5' => iconv('UTF-8', 'BIG5', '繁體中文'),
'ASCII' => 'Hello World',
];
foreach ($tests as $label => $str) {
$detected = detectEncoding($str);
echo "標簽: $label | 檢測: " . ($detected ?? '未知') . " | " . ($detected === $label ? '✅' : '❌') . PHP_EOL;
}
function detectFileEncoding(string $filepath, int $sampleSize = 4096): ?string
{
$handle = fopen($filepath, 'rb');
if (!$handle) return null;
// 讀取 BOM
$bom = fread($handle, 4);
$bomMap = [
"\xEF\xBB\xBF" => 'UTF-8',
"\xFF\xFE" => 'UTF-16LE',
"\xFE\xFF" => 'UTF-16BE',
];
foreach ($bomMap as $sig => $enc) {
if (strpos($bom, $sig) === 0) {
fclose($handle);
return $enc;
}
}
// 無 BOM,讀取樣本檢測
fseek($handle, 0);
$sample = fread($handle, $sampleSize);
fclose($handle);
return detectEncoding($sample);
}
| 場景 | 問題 | 對策 |
|---|---|---|
| 短字符串/純 ASCII | 多編碼兼容,無法區分 | 優先返回 UTF-8;結合業務上下文判斷 |
| GBK vs GB2312 | GBK 是 GB2312 超集,往返均通過 | 合併為「GBK 系」,或按字符分布統計區分 |
| 混合編碼文本 | 單一檢測失效 | 分塊檢測、或用 mb_detect_encoding 輔助 |
| 大文件 | 讀取全文消耗內存 | 僅讀取前 4KB/64KB 樣本 |
Encoding::toUTF8($mixed) 強制轉 UTF-8,內部自動檢測\Symfony\Component\String\AbstractString::from($bytes)->toUtf8()iconv 往返驗證法 是無依賴、輕量級檢測編碼的經典技巧。封裝成函數後,配合 BOM 檢測、樣本讀取、候選編碼優先級,能覆蓋 90% 的實際場景。對於高準確度要求、混合編碼、大文件場景,請引入專業檢測庫。記住:編碼檢測本質是概率推斷,業務上下文約束才是最終真理。