Preface :
編碼問題一直是編程人員處理上很頭痛的問題, 特別是在處理 BOM 的時候. 舉例來說, 考慮有一個 UTF-8 編碼檔案內容如下 :
如果你用 Hex mode 打開可以看到最前面有 UTF-8 的 BOM : 0xEF 0xBB 0xBF
而更完整的 UTF 系列 BOM 表可以參考下圖 (擷取自 Wiki) :
事實上 BOM 表的用途是用來決定程式在讀取文字檔時, endianness (byte order) 的方向與文字檔的編碼方式, 並且 BOM 是 Optional 的! 意味程式在處理時需要自己判斷是否文字檔有包含 BOM, 如果不處理就可能會出現亂碼. 如果我們以 BufferedReader 來讀取上面的文字檔, 代碼如下 :
- ReadUTF8.java :
- package test.kw;
- import java.io.BufferedReader;
- import java.io.FileInputStream;
- import java.io.InputStreamReader;
- public class ReadUTF8 {
- /**
- * @param args
- */
- public static void main(String[] args) throws Exception{
- FileInputStream fis = new FileInputStream("utf8.txt");
- BufferedReader br = new BufferedReader(new InputStreamReader(fis,"UTF8"));
- String line;
- while((line=br.readLine())!=null)
- {
- System.out.printf("\t[Info] Read: %s\n", line);
- }
- br.close();
- }
- }
事實上在 Java bug database 有對應的 record 存在 : "UTF-8 encoding does not recognize initial BOM" 與 "UTF-8 decoder handling of byte-order mark has changed"
Solution :
在現有的 Solution 上, 可以考慮 Apache IO Commons 中的一個類別 BOMInputStream. 透過它, 你可以設定 BOM 的自動 detection 並在 detection 出來後將之從 byte stream 中省略以避免上面看到的亂碼. 或者你可以自己處理. 下面為上一個範例的改善, 在第一行遇到 UTF-8 的 BOM 時將之忽略並將輸出
- UTF8ToAnsiUtils.java :
- package test.kw;
- import java.io.BufferedReader;
- import java.io.FileInputStream;
- import java.io.InputStreamReader;
- public class ReadUTF8 {
- // FEFF because this is the Unicode char represented by the UTF-8 byte order mark (EF BB BF).
- public static final String UTF8_BOM = "\uFEFF";
- /**
- * @param args
- */
- public static void main(String[] args) throws Exception{
- FileInputStream fis = new FileInputStream("utf8.txt");
- BufferedReader br = new BufferedReader(new InputStreamReader(fis,"UTF8"));
- String line;
- boolean firstLine = true;
- while((line=br.readLine())!=null)
- {
- if(firstLine) line = removeUTF8BOM(line);
- System.out.printf("\t[Info] Read: %s\n", line);
- }
- br.close();
- }
- private static String removeUTF8BOM(String s) {
- if (s.startsWith(UTF8_BOM)) {
- s = s.substring(1); // 如果 String 是以 BOM 開頭, 則省略字串最前面的第一個 字元.
- }
- return s;
- }
- }
Supplement :
* Byte order mark screws up file reading in Java
* Utf-8 Source File Encoding for Java
沒有留言:
張貼留言