在處理文本文件時,編碼不匹配會導致亂碼。Java 預設使用平台編碼(Windows 通常為 GBK,Linux/Mac 為 UTF-8),跨平台讀取時極易出錯。本文實現一個智能編碼檢測工具,支持 UTF-8、UTF-16LE/BE、GBK 等主流編碼自動識別。
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class CharsetDetector {
/**
* 檢測文件編碼
* @param file 目標文件
* @return 編碼名稱(如 UTF-8, GBK, UTF-16LE 等)
*/
public static String detectCharset(File file) {
String charset = "GBK"; // 默認編碼
byte[] first3Bytes = new byte[3];
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
bis.mark(0);
int read = bis.read(first3Bytes, 0, 3);
if (read == -1) return charset; // 空文件
boolean detected = false;
// 1. BOM 頭檢測
if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB && first3Bytes[2] == (byte) 0xBF) {
charset = "UTF-8";
detected = true;
} else if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
charset = "UTF-16LE";
detected = true;
} else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
charset = "UTF-16BE";
detected = true;
}
// 2. 無 BOM 時進行字節特徵分析
if (!detected) {
bis.reset();
int loc = 0;
while ((read = bis.read()) != -1) {
loc++;
// 遇到 4 字節 UTF-8 序列(0xF0+),極大概率是 UTF-8
if (read >= 0xF0) {
charset = "UTF-8";
break;
}
// 單獨出現 0x80-0xBF(非 ASCII 續位),傾向 GBK
if (0x80 <= read && read <= 0xBF) {
break; // 保持默認 GBK
}
// 2 字節序列:0xC0-0xDF 後跟 0x80-0xBF
if (0xC0 <= read && read <= 0xDF) {
int next = bis.read();
if (next != -1 && 0x80 <= next && next <= 0xBF) {
// 可能是 UTF-8 也可能是 GBK,繼續觀察
continue;
} else {
break; // 非法序列,按 GBK 處理
}
}
// 3 字節序列:0xE0-0xEF 後跟兩個 0x80-0xBF
if (0xE0 <= read && read <= 0xEF) {
int next1 = bis.read();
int next2 = bis.read();
if (next1 != -1 && next2 != -1 &&
0x80 <= next1 && next1 <= 0xBF &&
0x80 <= next2 && next2 <= 0xBF) {
charset = "UTF-8";
break;
} else {
break;
}
}
}
// 調試輸出:檢測位置與觸發字節
// System.out.println("檢測位置: " + loc + ", 字節: 0x" + Integer.toHexString(read));
}
} catch (IOException e) {
e.printStackTrace();
}
return charset;
}
// 測試主方法
public static void main(String[] args) {
File testFile = new File("test.txt");
System.out.println("檢測編碼: " + detectCharset(testFile));
}
}
| 編碼 | BOM 字節(十六進制) | 說明 |
|---|---|---|
| UTF-8 | EF BB BF | 最常見,建議始終帶 BOM |
| UTF-16LE | FF FE | 小端序,Intel x86 架構常用 |
| UTF-16BE | FE FF | 大端序,網絡傳輸標準序 |
| GBK | 無 | 不支持 BOM |
// 讀取文件時自動適配編碼
public String readFileWithAutoCharset(File file) throws IOException {
String charset = CharsetDetector.detectCharset(file);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(file), charset))) {
StringBuilder content = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append(System.lineSeparator());
}
return content.toString();
}
}
對於關鍵業務,推薦使用成熟庫:
<dependency>
<groupId>com.optimaize.languagedetector</groupId>
<artifactId>language-detector</artifactId>
<version>0.6</version>
</dependency>
或 Google 的 compact_enc_det (CED) 移植版,準確率超過 99%。
本文實現的編碼檢測器涵蓋了 BOM 優先檢測與字節特徵啟發式分析,能滿足大多數中文環境下的 UTF-8/GBK 自動識別需求。理解其原理有助於排查亂碼問題,但在高可靠性場景下請優先選用成熟的統計學檢測庫。