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

PHP 中文亂碼終極解決方案:編碼一致性、iconv/mb_convert_encoding、資料庫連線、瀏覽器輸出全鏈路分析

     編輯:PHP 相關知識

中文亂碼本質是編碼不一致:資料庫儲存編碼、PHP 內部編碼、HTML 輸出編碼、瀏覽器解碼編碼、檔案儲存編碼,任一環節不匹配即產生亂碼。本文提供全鏈路診斷清單、統一編碼最佳實踐、轉碼函數對比、遺留系統兼容方案。

亂碼產生的完整鏈路

資料來源 → [儲存編碼] → 資料庫 → [連線編碼] → PHP 變數 → [內部編碼] → 處理邏輯 → [輸出編碼] → HTTP 響應 → [Content-Type charset] → 瀏覽器解碼 → 顯示
      ↑                                                                      ↑
   檔案編碼                                                              meta charset

統一編碼鐵律:全站 UTF-8

環節配置驗證方式
PHP 檔案編輯器儲存為 UTF-8 無 BOMNotepad++/VS Code/PhpStorm 右下角顯示 UTF-8
HTML 輸出<meta charset="UTF-8"> 放在 <head> 首行瀏覽器開發工具 Network → Response Headers: Content-Type: text/html; charset=UTF-8
PHP 內部mb_internal_encoding('UTF-8') (PHP 5.6+ 預設即為 UTF-8)echo mb_internal_encoding();
資料庫連線MySQL: SET NAMES utf8mb4 / PDO DSN charset=utf8mb4SHOW VARIABLES LIKE 'character_set%'; 全為 utf8mb4
資料表/欄位CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ciSHOW CREATE TABLE tbl;
HTTP 響應頭Nginx: charset utf-8; / Apache: AddDefaultCharset UTF-8 / PHP: header('Content-Type: text/html; charset=UTF-8')curl -I 檢查回應頭
表單提交<form accept-charset="UTF-8">瀏覽器會以 UTF-8 編碼送出
URL 參數瀏覽器自動 URL 編碼,PHP urldecode() 自動解碼確保前端 encodeURIComponent()
檔案讀寫指定編碼 fopen($f, 'r', false, stream_context_create(['http'=>['header'=>'Accept-Charset: UTF-8']]))或用 mb_convert_encoding() 讀取後轉碼

轉碼函數對比:iconv vs mb_convert_encoding

<?php
// 1. iconv:系統級、支援 //TRANSLIT //IGNORE、較嚴格
$utf8 = iconv('GBK', 'UTF-8//IGNORE', $gbkStr); // 忽略無法轉換字元
$utf8 = iconv('GBK', 'UTF-8//TRANSLIT', $gbkStr); // 近似轉換 (如 € → EUR)

// 2. mb_convert_encoding:PHP 內建、支援自動偵測、處理陣列/物件遞迴
$utf8 = mb_convert_encoding($gbkStr, 'UTF-8', 'GBK'); // 指定來源編碼
$utf8 = mb_convert_encoding($gbkStr, 'UTF-8', 'GBK, BIG5, UTF-8'); // 自動偵測

// 3. 自動偵測編碼 (不完全可靠)
$encoding = mb_detect_encoding($str, ['UTF-8', 'GBK', 'BIG5', 'ASCII'], true);
if ($encoding) $utf8 = mb_convert_encoding($str, 'UTF-8', $encoding);

// 4. 通用轉碼函數:支援字串、陣列、物件遞迴
function autoCharset(mixed $data, string $from = 'GBK', string $to = 'UTF-8'): mixed {
    $from = strtoupper($from) === 'UTF8' ? 'UTF-8' : $from;
    $to   = strtoupper($to)   === 'UTF8' ? 'UTF-8' : $to;

    if ($from === $to || empty($data) || (is_scalar($data) && !is_string($data))) {
        return $data;
    }

    if (is_string($data)) {
        return function_exists('mb_convert_encoding')
            ? mb_convert_encoding($data, $to, $from)
            : @iconv($from, $to . '//IGNORE', $data);
    }

    if (is_array($data)) {
        $result = [];
        foreach ($data as $key => $val) {
            $newKey = autoCharset($key, $from, $to);
            $result[$newKey] = autoCharset($val, $from, $to);
        }
        return $result;
    }

    if (is_object($data)) {
        foreach ($data as $key => $val) {
            $data->$key = autoCharset($val, $from, $to);
        }
        return $data;
    }

    return $data;
}

// 使用:資料庫查詢結果轉碼
$rows = $pdo->query('SELECT * FROM gbk_table')->fetchAll(PDO::FETCH_ASSOC);
$utf8Rows = autoCharset($rows, 'GBK', 'UTF-8');
?>

常見亂碼場景診斷表

現象可能原因快速驗證解決
菱形問號 / UTF-8 字節序列被截斷、或雙重編碼檢查資料庫欄位長度是否截斷多位元組字元欄位用 VARCHAR(191) 或 TEXT;避免雙重 utf8_encode
亂碼:锟斤拷 / 涓婁竴GBK/Big5 被當作 UTF-8 讀取、或 UTF-8 被當作 GBK 讀取在 PHP 中 mb_detect_encoding 實際編碼統一連線編碼、補充轉碼層
問號 ??? / 方框 □字型不支援字元、或編碼轉換丟失字元換字體測試、檢查 //IGNORE 是否丟失用支援 CJK 的字體;避免 //IGNORE 靜默丟失
Emoji 變成 ????資料庫 utf8 (3字節) 不支援 Emoji (4字節)SHOW CREATE TABLE 確認 CHARSET改用 utf8mb4,MySQL 5.5.3+ 支援
JSON 輸出中文成 \uXXXXjson_encode 預設轉義 Unicode正常行為,非亂碼加 JSON_UNESCAPED_UNICODE 旗標
CSV 下載 Excel 打開亂碼Excel 預設用系統編碼 (中文 Windows 為 GBK/CP936)用記事本開啟檢查實際編碼輸出 UTF-8 with BOM (echo "\xEF\xBB\xBF" . $csv;) 或轉 GBK

遺留系統兼容:雙編碼共存策略

若無法立即將舊系統 (GBK/Big5) 全面遷移至 UTF-8,可採用「邊界轉碼」:

<?php
// 進入點:統一將輸入轉為 UTF-8 內部處理
function inputToUtf8(mixed $input): mixed {
    // 假設舊系統前端為 GBK
    return autoCharset($input, 'GBK', 'UTF-8');
}

// 出口:根據目標編碼輸出
function outputFromUtf8(mixed $data, string $targetEncoding = 'UTF-8'): mixed {
    if ($targetEncoding === 'UTF-8') return $data;
    return autoCharset($data, 'UTF-8', $targetEncoding);
}

// 中介軟體範例 (Slim/Laravel/原生)
$app->add(function ($request, $handler) {
    // 請求進入:GET/POST/JSON 轉 UTF-8
    $_GET    = inputToUtf8($_GET);
    $_POST   = inputToUtf8($_POST);
    $_COOKIE = inputToUtf8($_COOKIE);
    if ($request->getHeaderLine('Content-Type') === 'application/json') {
        $parsed = json_decode($request->getBody()->getContents(), true);
        $request = $request->withParsedBody(inputToUtf8($parsed));
    }
    $response = $handler->handle($request);
    // 響應輸出:若客戶端需 GBK (如舊版 IE、特定 API)
    if ($request->getHeader('Accept-Charset')[0] ?? '' === 'GBK') {
        $body = outputFromUtf8((string)$response->getBody(), 'GBK');
        $response = $response->withBody(new \Slim\Psr7\Stream(fopen('php://memory', 'rw')));
        $response->getBody()->write($body);
        $response = $response->withHeader('Content-Type', 'text/html; charset=GBK');
    }
    return $response;
});
?>

資料庫層面徹底修復


-- 1. 檢查現狀
SHOW VARIABLES LIKE 'character_set%';
SHOW CREATE TABLE your_table;

-- 2. 修改資料庫預設編碼
ALTER DATABASE your_db CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

-- 3. 修改資料表編碼 (含現有資料轉換)
ALTER TABLE your_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 4. 修改欄位編碼 (若 CONVERT 失敗)
ALTER TABLE your_table CHANGE col_name col_name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 5. 連線指定編碼 (PDO)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci',
]);

總結

中文亂碼無捷徑,唯有全鏈路 UTF-8 一致性。核心步驟:

  1. 所有 PHP 檔案儲存為 UTF-8 無 BOM
  2. HTML 輸出 <meta charset="UTF-8"> + HTTP Header Content-Type: text/html; charset=UTF-8
  3. 資料庫、資料表、欄位、連線全部 utf8mb4
  4. PHP 內部 mb_internal_encoding('UTF-8')
  5. 外部輸入 (GBK/Big5) 在邊界統一轉碼為 UTF-8 內部處理
  6. 輸出非 UTF-8 目標時,在邊界轉碼
  7. 善用 autoCharset() 遞迴處理陣列/物件
  8. JSON 輸出加 JSON_UNESCAPED_UNICODE,CSV 下載加 BOM

徹底解決亂碼,不再是「試錯」,而是「配置」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved