1. 程式人生 > >JAVA輸出帶BOM的UTF-8編碼的檔案

JAVA輸出帶BOM的UTF-8編碼的檔案

當從http 的response輸出CSV檔案的時候,設定為utf8的時候預設是不帶
bom的,但是windows的Excel是使用bom來確認utf8編碼的,所有需要把bom寫到檔案的開頭。
微軟在 UTF-8 中使用 BOM 是因為這樣可以把 UTF-8 和 ASCII 等編碼明確區分開。
否則用Excel開啟CSV檔案有可能是亂碼的
示例程式碼如下:
response.setContentType("text/csv");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.setCharacterEncoding("UTF-8");
byte[] uft8bom={(byte)0xef,(byte)0xbb,(byte)0xbf};
OutputStream out = response.getOutputStream();
out.write(uft8bom);

OutputStreamWriter writer = new OutputStreamWriter(out,"UTF-8");

//write other content ...

什麼是BOM:
BOM(byte-order mark),即位元組順序標記,它是插入到以UTF-8、UTF16或UTF-32編碼Unicode檔案開頭的特殊標記,用來識別Unicode檔案的編碼型別。對於UTF-8來說,BOM並不是必須的,因為BOM是用來標記多位元組編碼檔案的編碼型別和位元組順序(big-endian或little- endian)。而UTF8中,每個字元的編碼有多少位是通過第一個位元組來表述的,而且沒有big-endian和little-endian的區分,見後述。

BOMs 檔案頭:
   00 00 FE FF    = UTF-32, big-endian
   FF FE 00 00    = UTF-32, little-endian
   EF BB BF       = UTF-8,
   FE FF          = UTF-16, big-endian
   FF FE          = UTF-16, little-endian

還有一個要注意的是:UTF-8 的網頁程式碼不應使用 BOM,否則常常會出錯:
在網頁上使用BOM是個錯誤。BOM設計出來不是用來支援HTML和XML的。要識別文字編碼,HTML有charset屬性,XML有encoding屬性,沒必要拉BOM撐場面。雖然理論上BOM可以用來識別UTF-16編碼的HTML頁面,但實際工程上很少有人這麼幹。畢竟UTF-16這種編碼連ASCII都雙位元組,實在不適用於做網頁。

Windows使用BOM的歷史原因:
通常BOM是用來標示Unicode純文字位元組流的,用來提供一種方便的方法讓文字處理程式識別讀入的.txt檔案是哪個Unicode編碼(UTF-8,UTF-16BE,UTF-16LE)。Windows相對對BOM處理比較好,是因為Windows把Unicode識別程式碼整合進了API裡,主要是CreateFile()。開啟文字檔案時它會自動識別並剔除BOM。Windows用這個有歷史原因,因為它最初脫胎於多內碼表的環境。而引入Unicode時Windows的設計者又希望能在使用者不注意的情況下同時相容Unicode和非Unicode(Multiple byte)文字檔案,就只能藉助這種小trick了。

帶BOM的文字檔案在Linux/unix環境又經常會遇到問題:
知乎介紹的很詳細:
http://www.zhihu.com/question/20167122

文字檔案解析:     
文字檔案對應於人類可以閱讀的文字,如何從2進位制轉換為文字檔案呢?起初由於計算機在美國發明,自然大家考慮的是英語如何表示,英語字母總共26個,加上特殊字元,128個字元,7位既一個byte即可表示出來。這個就是大家所熟知的ascill編碼。對應關係很簡單,一個字元對應一一個byte。     但很快發現,其他非英語國家的文字遠遠超過ascill碼,這時候大家當然想統一一下,不同國家出了自己不同的編碼方式,中國的gb2312就是自己做出來的編碼方式,這樣下去每個國家都有自己的編碼方式,來回轉換太麻煩了。這時候出現了新的編碼方式,unicode編碼方式,想將編碼統一,所以規定了每個字元對應的unicode碼。     1、很多檔案都是ascii編碼,如果用unicode 太浪費。     2、沒有標誌位說明該幾個位元組來解析為一個符號。     這時候拯救世界的utf出現了,utf是unicode的一種實現,只不過更聰明瞭。utf16是佔用兩位元組,或者四位元組,utf32是佔用四位元組。utf8是很聰明的一種表示方式。     
1、對於單位元組符號,位元組第一位為0,後面7位表示位元組編碼。     
2、對於n位元組符號,第一位元組的前n位都設為1,第n+1位為0,其餘位用於編碼。
對於不同的編碼,在文字的最前方有不同的標誌,unicode 通常有兩位來表示分別是ff fe, 或者feff, fffe表示litte-endian 編碼feff表示big-endian編碼。
utf8是efbbbf來開頭的。可以看出來utf-8是自解釋的,所以不用帶這個標誌檔案,大多數程式是可以識別的。但有些程式不能識別這個標誌,比如php就會直接把這個標誌當文字解析,不會忽略。