讀取未知編碼文件導致亂碼是 Java 開發常見痛點。cpDetector (Codepage Detector) 透過統計位元組分佈、BOM、XML 聲明等多種探測器鏈,自動判斷文件編碼。本文涵蓋 Maven 整合、核心 API、探測器優先級配置、效能優化與常見誤判場景。
<dependency>
<groupId>com.googlecode.cpdetector</groupId>
<artifactId>cpdetector</artifactId>
1.0.10 <!-- 或最新版 -->
</dependency>
import java.io.File;
import java.nio.charset.Charset;
import cpdetector.io.*;
public class EncodingDetector {
// 單例探測器 (線程安全)
private static final CodepageDetectorProxy DETECTOR = CodepageDetectorProxy.getInstance();
static {
// 1. JChardet: 統計式探測 (支援最多編碼,但較慢)
DETECTOR.add(new JChardetFacade());
// 2. ASCII: 純 ASCII 快速判斷
DETECTOR.add(new ASCIIDetector());
// 3. Unicode BOM: UTF-8/16/32 BOM 偵測
DETECTOR.add(new UnicodeDetector());
// 4. XML/HTML meta 標籤探測
DETECTOR.add(new ParsingDetector(false));
// 5. 預設回退編碼 (平台預設)
DETECTOR.add(JChardetFacade.getInstance()); // 重複添加無妨,最後作為 fallback
}
public static Charset detect(File file) {
try {
return DETECTOR.detectCodepage(file.toURI().toURL());
} catch (Exception e) {
return Charset.defaultCharset(); // 失敗回退
}
}
public static void main(String[] args) {
File f = new File("data.txt");
Charset cs = detect(f);
System.out.println("偵測編碼: " + cs.name());
// 讀取示範
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(f), cs))) {
String line;
while ((line = br.readLine()) != null) System.out.println(line);
} catch (IOException e) { e.printStackTrace(); }
}
}
| 探測器 | 原理 | 速度 | 適用場景 | 優先級建議 |
|---|---|---|---|---|
| UnicodeDetector | 讀取前 4 bytes 檢查 BOM (EF BB BF, FF FE, FE FF...) | 極快 | 含 BOM 的 UTF 檔案 | 1 (最高) |
| ParsingDetector | 解析 XML 或 HTML <meta charset> | 快 | XML/HTML 文件 | 2 |
| ASCIIDetector | 全文 0x00-0x7F 判定為 ASCII/UTF-8 相容 | 極快 | 純英文/數字文件 | 3 |
| JChardetFacade | Mozilla 統計模型 (字元分佈、雙字節特徵) | 慢 (需讀較多位元組) | 無 BOM、無聲明的中日韓/歐洲語系 | 4 (最後兜底) |
getBytesToAnalyze() 限制為 64KB~256KB。Map 快取 (注意檔案修改時間失效)。CompletableFuture 或線程池並行。-Dfile.encoding=UTF-8 影響預設回退,建議顯式指定 StandardCharsets.UTF_8。| 現象 | 原因 | 解決 |
|---|---|---|
| GBK 檔案被判為 UTF-8 | 中文字節剛好符合 UTF-8 多字節模式 | 優先添加 ParsingDetector 或指定 fallback 為 GBK/GB18030 |
| 極短文件 ( < 100 bytes) 側測不準 | 統計樣本不足 | 短文件強制指定編碼或人工確認 |
| 混合編碼文件 | 單一檔案包含多種編碼 (極罕見) | 拆分文件或逐行嘗試解碼 |
| 偵測耗時 > 1s | 大檔案觸發全文掃描 | 限制 JChardet 掃描長度、啟用快取 |
com.googlecode.juniversalchardet:juniversalchardet:1.0.3。CharsetDetector (內建):JDK 10 引入 java.nio.charset.CharsetDetector (內部類),尚未公開 API,不可直接使用。// 生產環境推薦配置
detector.add(new UnicodeDetector()); // 1. BOM 最優先
detector.add(new ParsingDetector(false)); // 2. XML/HTML 聲明
detector.add(new ASCIIDetector()); // 3. 純 ASCII 快速通道
detector.add(new JChardetFacade() {
@Override protected int getBytesToAnalyze() { return 64 * 1024; } // 限制 64KB
}); // 4. 統計兜底
搭配快取、非同步批次處理,可滿足千萬級文件編碼偵測需求。