檔案編碼偵測是處理文字檔、匯入匯出、跨系統整合的常見痛點。本文系統整理三層次解法:簡易 BOM 判斷、cpdetector 統計式偵測、Properties 載入編碼處理,並提供可直接落地的完整程式碼。
適用於僅需區分 UTF-8 與系統預設編碼(中文環境通常為 GBK/Big5)的情境:
public class SimpleEncodingDetector {
// UTF-8 BOM: EF BB BF -> 有號位元組: -17, -69, -65
private static final byte[] UTF8_BOM = new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF };
public static String detectEncoding(String filePath) throws IOException {
try (FileInputStream fis = new FileInputStream(filePath)) {
byte[] bom = new byte[3];
int read = fis.read(bom);
if (read == 3 && matches(bom, UTF8_BOM)) {
return "UTF-8";
}
}
// 無 BOM 視為系統預設編碼
return Charset.defaultCharset().name(); // 通常為 GBK (Windows) 或 UTF-8 (Linux)
}
private static boolean matches(byte[] a, byte[] b) {
if (a.length != b.length) return false;
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i]) return false;
}
return true;
}
}
cpdetector 整合多種偵測器(JChardet、ASCII、Unicode、Parsing),準確度遠高於單一演算法:
<dependency>
<groupId>com.googlecode.cpdetector</groupId>
<artifactId>cpdetector</artifactId>
<version>1.0.10</version>
</dependency>
<!-- 相依:antlr, chardet -->
import com.chenlb.cpdetector.*;
public class EncodingDetector {
private static final CodepageDetectorProxy detector = buildDetector();
private static CodepageDetectorProxy buildDetector() {
CodepageDetectorProxy proxy = CodepageDetectorProxy.getInstance();
proxy.add(new ParsingDetector(false)); // HTML/XML meta 標籤偵測
proxy.add(JChardetFacade.getInstance()); // Mozilla JChardet 統計偵測(核心)
proxy.add(ASCIIDetector.getInstance()); // ASCII 純英文偵測
proxy.add(UnicodeDetector.getInstance()); // UTF-16/32 BOM 偵測
return proxy;
}
// 檔案路徑偵測
public static String detect(File file) {
try {
Charset charset = detector.detectCodepage(file.toURI().toURL());
return charset != null ? charset.name() : null;
} catch (Exception e) {
return null;
}
}
// URL 偵測(支援 classpath、jar 內資源)
public static String detect(URL url) {
try {
Charset charset = detector.detectCodepage(url);
return charset != null ? charset.name() : null;
} catch (Exception e) {
return null;
}
}
// InputStream 偵測(需指定取樣位元組數)
public static String detect(InputStream in, int sampleBytes) {
try {
Charset charset = detector.detectCodepage(in, sampleBytes);
return charset != null ? charset.name() : null;
} catch (Exception e) {
return null;
}
}
}
public class EncodingUsageDemo {
public static void main(String[] args) throws Exception {
// 1. 讀取內部檔案
File config = new File("config.properties");
String encoding = EncodingDetector.detect(config);
System.out.println("偵測編碼:" + encoding);
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(config), encoding))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
// 2. 讀取 classpath 資源
URL url = EncodingUsageDemo.class.getResource("/resource/設定檔.xml");
String enc = EncodingDetector.detect(url);
try (BufferedReader br = new BufferedReader(
new InputStreamReader(url.openStream(), enc))) {
// 處理內容...
}
// 3. 列出 JVM 支援編碼
System.out.println("支援編碼:" + Charset.availableCharsets().keySet());
System.out.println("預設編碼:" + Charset.defaultCharset());
}
}
Properties.load(InputStream) 內部使用 ISO-8859-1 讀取,中文會亂碼。三種解法:
Properties props = new Properties();
try (FileInputStream fis = new FileInputStream("config.properties")) {
props.load(fis);
}
String value = props.getProperty("key");
String realValue = new String(value.getBytes("ISO-8859-1"), "UTF-8"); // 或 GBK
// 專案規範:所有 .properties 必須用 UTF-8 儲存
try (FileInputStream fis = new FileInputStream("config.properties");
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
props.load(isr);
}
// 直接讀取即為正確中文
File file = new File("config.properties");
String encoding = EncodingDetector.detect(file);
try (FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis, encoding)) {
props.load(isr);
}
| 方案 | 準確度 | 效能 | 適用場景 |
|---|---|---|---|
| BOM 判斷 | 低(無 BOM 失效) | 極快 | 內部產出檔案、可控環境 |
| cpdetector | 高(統計學基礎) | 中等(需取樣) | 外部檔案、使用者上傳、未知來源 |
| 約定 UTF-8 | 100% | 最快 | 團隊專案、新建系統、微服務溝通 |
檔案編碼偵測無銀彈,**約定優於偵測**是現代軟體工程最佳實踐。新專案強制 UTF-8、舊系統引入 cpdetector 過渡、邊界整合點做好轉碼防護,即可徹底解決亂碼困擾。