程式扎記: [ Java 常見問題 ] Handle UTF8 file with BOM

標籤

2012年7月17日 星期二

[ Java 常見問題 ] Handle UTF8 file with BOM

參考至 這裡 
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 : 
  1. package test.kw;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.FileInputStream;  
  5. import java.io.InputStreamReader;  
  6.   
  7. public class ReadUTF8 {  
  8.   
  9.     /** 
  10.      * @param args 
  11.      */  
  12.     public static void main(String[] args) throws Exception{  
  13.         FileInputStream fis = new FileInputStream("utf8.txt");  
  14.         BufferedReader br = new BufferedReader(new InputStreamReader(fis,"UTF8"));  
  15.         String line;  
  16.         while((line=br.readLine())!=null)  
  17.         {  
  18.             System.out.printf("\t[Info] Read: %s\n", line);  
  19.         }  
  20.         br.close();  
  21.     }  
  22.   
  23. }  
這時候如果你將上面類別包裝成 jar 檔, 執行它. 會發現讀出內容有亂碼, 其實那就是 BOM 造成的 : 
 

事實上在 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 : 
  1. package test.kw;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.FileInputStream;  
  5. import java.io.InputStreamReader;  
  6.   
  7. public class ReadUTF8 {  
  8.     // FEFF because this is the Unicode char represented by the UTF-8 byte order mark (EF BB BF).  
  9.     public static final String UTF8_BOM = "\uFEFF";  
  10.   
  11.     /** 
  12.      * @param args 
  13.      */  
  14.     public static void main(String[] args) throws Exception{  
  15.         FileInputStream fis = new FileInputStream("utf8.txt");  
  16.         BufferedReader br = new BufferedReader(new InputStreamReader(fis,"UTF8"));  
  17.         String line;  
  18.         boolean firstLine = true;  
  19.         while((line=br.readLine())!=null)  
  20.         {  
  21.             if(firstLine) line = removeUTF8BOM(line);  
  22.             System.out.printf("\t[Info] Read: %s\n", line);  
  23.         }  
  24.         br.close();  
  25.     }  
  26.   
  27.     private static String removeUTF8BOM(String s) {  
  28.         if (s.startsWith(UTF8_BOM)) {   
  29.             s = s.substring(1); // 如果 String 是以 BOM 開頭, 則省略字串最前面的第一個 字元.  
  30.         }  
  31.         return s;  
  32.     }  
  33. }  
接著將之包裝為 test.jar 並將 main-class 設為上面的類別, 如下執行可以發現亂碼不建了 : 
 

Supplement : 
Byte order mark screws up file reading in Java 
Utf-8 Source File Encoding for Java

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!