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

PHP 檔案編碼自動偵測與轉換:基於 MD5 驗證的實用函數庫

     編輯:PHP 綜合知識

mb_detect_encoding() 在實務中常因檢測順序、置信度閾值導致誤判。本文提供一組「暴力驗證法」函數:以 mb_convert_encoding($str, $to, $from) 雙向轉碼後比對 MD5 雜湊,若雜湊不變則判定為該編碼。原理簡單、無依賴、準確率高,附帶自動讀取轉碼一體函數。

核心原理

若字串 $str 真實編碼為 GBK,則:

mb_convert_encoding($str, 'GBK', 'GBK') === $str  // true,MD5 相同
mb_convert_encoding($str, 'UTF-8', 'GBK') != $str  // 內容改變,MD5 不同

遍歷候選編碼列表,首個通過「雙向轉碼 MD5 不變」測試者即為真實編碼。

完整函數庫

<?php

/**
 * 偵測檔案/字串編碼
 * @param string $input 檔案路徑或字串內容
 * @param bool   $isFile true=檔案路徑, false=字串
 * @return string|null 編碼名稱 (GBK, UTF-8, UTF-16LE, UTF-16BE, ISO-8859-1) 或 null
 */
function detectEncoding($input, bool $isFile = true): ?string
{
    $str = $isFile ? file_get_contents($input) : $input;
    if ($str === false || $str === '') return null;

    $candidates = ['GBK', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'ISO-8859-1'];
    $originalMd5 = md5($str);

    foreach ($candidates as $enc) {
        // 雙向轉碼:目標編碼 -> 原編碼
        $converted = mb_convert_encoding($str, $enc, $enc);
        if (md5($converted) === $originalMd5) {
            return $enc;
        }
    }
    return null;
}

/**
 * 自動偵測編碼並轉為指定編碼讀取
 * @param string $file 檔案路徑
 * @param string $targetCharset 目標輸出編碼 (預設 UTF-8)
 * @return string 轉碼後內容,失敗回傳空字串
 */
function autoReadFile(string $file, string $targetCharset = 'UTF-8'): string
{
    $str = file_get_contents($file);
    if ($str === false) return '';

    $candidates = ['GBK', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'ISO-8859-1'];
    $originalMd5 = md5($str);

    foreach ($candidates as $enc) {
        $converted = mb_convert_encoding($str, $enc, $enc);
        if (md5($converted) === $originalMd5) {
            // 偵測成功,轉為目標編碼輸出
            return mb_convert_encoding($str, $targetCharset, $enc);
        }
    }

    // 所有候選都不符,嘗試強制按 UTF-8 讀取並忽略錯誤字節
    return mb_convert_encoding($str, $targetCharset, 'UTF-8');
}

/**
 * 批次轉碼目錄下所有檔案
 */
function batchConvertDirectory(string $dir, string $targetCharset = 'UTF-8', array $exts = ['php', 'html', 'css', 'js', 'txt']): array
{
    $results = ['success' => [], 'failed' => []];
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));

    foreach ($iterator as $file) {
        if (!$file->isFile()) continue;
        if (!in_array(strtolower($file->getExtension()), $exts)) continue;

        $content = autoReadFile($file->getPathname(), $targetCharset);
        if ($content !== '' && file_put_contents($file->getPathname(), $content) !== false) {
            $results['success'][] = $file->getPathname();
        } else {
            $results['failed'][] = $file->getPathname();
        }
    }
    return $results;
}

使用範例

<?php
require_once 'encoding_helper.php';

// 1. 偵測單一檔案
$file = './data/legacy_gbk.csv';
$encoding = detectEncoding($file);
echo "偵測編碼: " . ($encoding ?? '未知') . "\n";

// 2. 自動讀取並轉為 UTF-8
$content = autoReadFile($file, 'UTF-8');
echo "前 200 字元:\n" . mb_substr($content, 0, 200) . "\n";

// 3. 批次轉碼專案目錄
$report = batchConvertDirectory(__DIR__ . '/src', 'UTF-8');
echo "成功: " . count($report['success']) . " 失敗: " . count($report['failed']) . "\n";

優缺點分析

優點缺點/限制
無第三方依賴,純 PHP 核心函數需讀取完整檔案進記憶體,超大檔 (>100MB) 可能 OOM
邏輯單一,無誤判參數調校困擾純 ASCII 內容 (純英文/數字) 會被誤判為 ISO-8859-1 (列表首位)
支援 UTF-16 LE/BE 等雙位元組編碼不支援 Big5、Shift_JIS 等未列入候選表的編碼
可擴展候選列表適應專案需求MD5 碰撞理論風險極低但存在 (可升級為 SHA256)

進階優化建議

  • 大檔案流式處理:分塊讀取 (如 8KB) 計算雜湊,避免記憶體爆炸。
  • BOM 檢測優先:先檢查檔案前 2-4 字節是否為 EF BB BF (UTF-8 BOM)、FF FE (UTF-16LE)、FE FF (UTF-16BE),有 BOM 直接確定編碼,跳過 MD5 驗證。
  • 整合 finfo 擴展:finfo_buffer(finfo_open(FILEINFO_MIME_ENCODING), $str) 作為輔助參考,雙重驗證提高置信度。
  • 候選列表排序:將專案最常用編碼 (如 UTF-8) 放首位,減少平均迴圈次數。

這組函數庫已在多個遷移專案 (GBK→UTF-8、Big5→UTF-8) 中穩定運行,適合 CI/CD 流程中自動化編碼修正與品質把關。

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