在早期華語系統整合專案中,繁體 (Big5) 與簡體 (GB2312) 編碼共存是常態。雖然現代系統普遍採用 Unicode (UTF-8),但維護舊系統、處理舊資料遷移時,仍需掌握這兩種編碼的直接互轉邏輯。本文深度解析一個純 Java 實作的 Big5 轉 GB2312 轉換器。
Big5 與 GB2312 皆為雙位元組編碼,但碼表排列方式截然不同,無簡單數學公式可直接換算。業界標準做法是建立映射對照表 (mapping table),查表轉換。
package com.charset.converter;
import java.io.*;
public class Big5ToGb2312Converter {
// 映射表檔案:每筆對應佔 2 位元組 (GB2312 碼)
private static final String MAP_FILE = "bg-gb.tab";
private static byte[] mapData; // 載入記憶體的映射表
static {
loadMappingTable();
}
/** 載入映射表至記憶體 */
private static void loadMappingTable() {
try (FileInputStream fis = new FileInputStream(MAP_FILE)) {
int len = fis.available();
mapData = new byte[len];
fis.read(mapData);
} catch (Exception e) {
throw new RuntimeException("載入編碼映射表失敗: " + MAP_FILE, e);
}
}
/**
* 計算 Big5 碼在映射表中的位移索引
* @param big5Code Big5 雙位元組碼值 (高位在前)
* @return 映射表中的位元組索引
*/
private static int calculateOffset(int big5Code) {
int high = (big5Code >>> 8) & 0xFF;
int low = big5Code & 0xFF;
// Big5 碼區結構調整
high -= 0xA1;
if (low <= 0x7E) {
low -= 0x40;
} else {
low -= (0xA1 - 0x7E - 1) + 0x40;
}
// 每個字元佔 2 位元組 (GB2312 雙位元組)
return 2 * (high * 157 + low);
}
/**
* 核心轉換邏輯:直接操作位元組陣列
* @param srcBytes Big5 編碼的位元組陣列 (會被直接修改)
* @return 轉換後的 GB2312 位元組陣列 (同一物件)
*/
public static byte[] convert(byte[] srcBytes) {
int i = 0;
while (i < srcBytes.length) {
int high = srcBytes[i] & 0xFF;
// 判斷是否為 Big5 雙位元組字元首位 (0xA1-0xFE)
if (high >= 0xA1 && high <= 0xFE) {
i++;
if (i >= srcBytes.length) break;
int low = srcBytes[i] & 0xFF;
// 合法 Big5 低位元組範圍: 0x40-0x7E, 0xA1-0xFE
if (low < 0x40 || low > 0xFE || (low > 0x7E && low < 0xA1)) {
i++;
continue;
}
int offset = calculateOffset((high << 8) | low);
// 從映射表取得 GB2312 碼,覆寫原位元組
srcBytes[i - 1] = mapData[offset];
srcBytes[i] = mapData[offset + 1];
i++;
} else {
// ASCII 或單位元組字元,保持不變
i++;
}
}
return srcBytes;
}
/**
* 字串層級轉換介面
* @param big5Str Big5 編碼的字串
* @return GB2312 編碼的字串
*/
public static String convert(String big5Str) {
if (big5Str == null || big5Str.isEmpty()) return big5Str;
try {
// 1. 字串 → Big5 位元組陣列
byte[] bytes = big5Str.getBytes("Big5");
// 2. 位元組陣列轉換
convert(bytes);
// 3. 位元組陣列 → GB2312 字串
return new String(bytes, "GB2312");
} catch (Exception e) {
throw new RuntimeException("編碼轉換失敗", e);
}
}
// 測試主程式
public static void main(String[] args) {
String traditional = "繁體中文測試:台灣、香港";
String simplified = convert(traditional);
System.out.println("原文 (Big5): " + traditional);
System.out.println("轉換 (GB2312): " + simplified);
}
}
Big5 編碼分區:
索引公式:offset = 2 × [(high - 0xA1) × 157 + adjustedLow]
為節省記憶體與 GC 壓力,轉換直接在原 byte[] 上覆寫,適合大量文字處理場景。
JDK 內建 Charset 機制已支援雙向轉換,且效能更好、維護性更高:
// 推薦做法:使用標準 API
String gb2312Str = new String(big5Str.getBytes("Big5"), "GB2312");
// 或使用 CharsetDecoder/Encoder 流式處理大檔案
CharsetDecoder decoder = Charset.forName("Big5").newDecoder();
CharsetEncoder encoder = Charset.forName("GB2312").newEncoder();
| 場景 | 建議方案 |
|---|---|
| 新專案開發 | 統一使用 UTF-8,避免編碼轉換 |
| 舊系統維護、資料遷移 | JDK 內建 Charset API |
| 極度效能敏感、嵌入式環境 | 本文查表法 (無物件建立開銷) |
| 特殊字元映射需求 (異體字、造字) | 自訂映射表 + 查表法 |
理解底層編碼轉換機制,不僅能解決亂碼疑難雜症,更能在字元處理、國際化 (i18n) 領域建立堅實技術直覺。