首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 自動偵測字串編碼(UTF-8 / GB2312 / GBK)並轉碼實作

     編輯:PHP 相關知識

處理來源未知編碼的字串(如爬蟲資料、舊系統匯入)時,需自動判斷編碼並統一轉為 UTF-8。本文提供基於位元組特徵的偵測函數,無需 mb_detect_encoding(不準確),直接解析位元組模式。

原理說明

  • ASCII(英文、數字、符號):位元組值 < 128(最高位 0),單位元組。
  • UTF-8 中文:3 位元組,模式 1110xxxx 10xxxxxx 10xxxxxx(首位元組 0xE0~0xEF,後續 0x80~0xBF)。
  • GB2312/GBK 中文:2 位元組,模式 110xxxxx 10xxxxxx(首位元組 0x81~0xFE,次位元組 0x40~0xFE,排除 0x7F)。

完整偵測與轉碼函數

<?php
/**
 * 自動偵測字串編碼並轉換為指定輸出編碼
 * @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++) {
        $byte = ord($string[$i]);

        // ASCII 直接跳過
        if ($byte < 128) continue;

        // 檢測 UTF-8 三位元組模式:1110xxxx 10xxxxxx 10xxxxxx
        if (($byte & 0xE0) === 0xE0) { // 0xE0 = 224
            // 確保還有至少 2 個位元組
            if ($i + 2 >= $len) break;
            $b1 = ord($string[$i+1]);
            $b2 = ord($string[$i+2]);
            if (($b1 & 0xC0) === 0x80 && ($b2 & 0xC0) === 0x80) { // 10xxxxxx
                $encoding = 'UTF-8';
                break; // 找到確定特徵,停止偵測
            }
        }

        // 檢測 GBK/GB2312 兩位元組模式:110xxxxx 10xxxxxx
        if (($byte & 0xC0) === 0xC0) { // 0xC0 = 192
            if ($i + 1 >= $len) break;
            $b1 = ord($string[$i+1]);
            if (($b1 & 0xC0) === 0x80) {
                $encoding = 'GB2312'; // GBK 相容 GB2312
                break;
            }
        }
    }

    // 已是目標編碼直接返回
    if (strtoupper($encoding) === strtoupper($outEncoding)) {
        return $string;
    }

    // 使用 iconv 轉碼,//IGNORE 忽略無法轉換字元
    return iconv($encoding, $outEncoding . '//IGNORE', $string);
}

// 使用範例
$str_gbk = "測試字串"; // 假設為 GBK 編碼
$str_utf8 = safeEncoding($str_gbk, 'UTF-8');
echo $str_utf8; // 正常顯示中文
?>

進階:加入 BOM 偵測(檔案場景)

function detectEncodingWithBOM($content) {
    // UTF-8 BOM: EF BB BF
    if (substr($content, 0, 3) === "\xEF\xBB\xBF") return 'UTF-8';
    // UTF-16 LE BOM: FF FE
    if (substr($content, 0, 2) === "\xFF\xFE") return 'UTF-16LE';
    // UTF-16 BE BOM: FE FF
    if (substr($content, 0, 2) === "\xFE\xFF") return 'UTF-16BE';
    // UTF-32 LE BOM: FF FE 00 00
    if (substr($content, 0, 4) === "\xFF\xFE\x00\x00") return 'UTF-32LE';
    // UTF-32 BE BOM: 00 00 FE FF
    if (substr($content, 0, 4) === "\x00\x00\xFE\xFF") return 'UTF-32BE';

    return safeEncoding($content); // 回退到位元組特徵偵測
}

效能與準確度考量

方法準確度速度適用場景
mb_detect_encoding中(易誤判短字串)中快速原型、非關鍵路徑
本文位元組特徵法高(明確模式匹配)快(單趟掃描)生產環境、大量資料清洗
檢測 BOM + 位元組特徵最高快檔案讀取、跨系統匯入
統計語言模型(如 PHP-ML)極高慢極短字串、混合編碼

常見坑與避坑指南

  • 純 ASCII 字串:偵測結果預設 UTF-8,實際上任何單位元組編碼皆相容,直接返回無害。
  • 混合編碼字串:極少見,若前半段 UTF-8 後半段 GBK,函數會在第一個非 ASCII 字元判定並停止,導致後半段轉碼錯誤。解決:分段偵測或強制指定編碼。
  • iconv 不支援的編碼名稱:使用 iconv -l 查看系統支援列表,常用別名:GB2312、GBK、CP936、UTF-8、UTF-16。
  • //IGNORE 與 //TRANSLIT://IGNORE 丟棄無法轉換字元,//TRANSLIT 嘗試轉換為相近字元(如全形轉半形),視需求選擇。

整合建議:封裝為工具類

<?php
class EncodingHelper {
    public static function toUtf8($string) {
        return self::safeEncoding($string, 'UTF-8');
    }
    public static function safeEncoding($string, $outEncoding = 'UTF-8') {
        // ... 上述函數本體 ...
    }
    public static function detectFile($filepath) {
        $content = file_get_contents($filepath);
        return self::detectEncodingWithBOM($content);
    }
}
?>
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved