Java讀帶有BOM的UTF-8文件亂碼緣由及解決方法

最近在處理文件時發現了一樣類型的文件使用的編碼多是不一樣的。因此想將文件的格式統一一下(由於UTF-8的通用性,決定往UTF-8統一),碰見的第一個問題是:如何查看現有文件的編碼方式。

上網找了一下,找到幾篇比較好文章,這裏就不轉載啦把連接搞過來。
文件編碼問題集錦
字符串編碼(charset,encoding,decoding)問題原理
Java編碼淺析
斷定文件編碼或文本流編碼的方法
上面的幾篇文章能夠當作認識編碼問題的「從入門到精通」

若是你看完了上面的文章,必定了解到了,在java中,class文件採用utf8的編碼方式,JVM運行時採用utf16。Java的字符串是永遠都是unicode的,採用的是UTF-16的編碼方式。

想測試一下,java對UTF-8文件的讀寫的能力,結果發現了一個很鬱悶的問題,若是經過java寫的UTF-8文件,使用Java能夠正確的讀,可是若是用記事本將相同的內容使用UTF-8格式保存,則在使用程序讀取是會從文件中多讀出一個不可見字符。
測試代碼以下: java

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;


public class UTF8Test {
	public static void main(String[] args) throws IOException {
		File f  = new File("./utf.txt");
		FileInputStream in = new FileInputStream(f);
		// 指定讀取文件時以UTF-8的格式讀取
		BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
		
		String line = br.readLine();
		while(line != null)
		{
			System.out.println(line);
			line = br.readLine();
		}
	}
}
utf.txt經過記事本建立,另存時使用指定utf-8編碼,其內容爲:
引用

This is the first line.
This is second line.



正常的測試結果應該是直接輸出utf.txt的文本內容。但是實際上卻輸出了下面的內容: 測試

引用

?This is the first line.
This is second line.


第一行多出了一個問號。
經過上面的幾篇文章應該能夠想到是Java讀取BOM(Byte Order Mark)的問題,在使用UTF-8時,能夠在文件的開始使用3個字節的"EF BB BF"來標識文件使用了UTF-8的編碼,固然也能夠不用這個3個字節。
上面的問題應該就是由於對開頭3個字節的讀取致使的。開始不太相信這個是JDK的Bug,後來在屢次試驗後,問題依然存在,就又狗狗了一下,果真找到一個以下的Bug:
Bug ID:4508058
不過在我關掉的一些頁面中記得有篇文件說這個bug只在jdk1.5及以前的版本纔有,說是1.6已經解決了,從目前來看1.6只是解決了讀取帶有BOM文件失敗的問題,仍是不能區別處理有BOM和無BOM的UTF-8編碼的文件,從Bug ID:4508058裏的描述能夠看出,這個問題將做爲一個不會修改的問題關閉,對於BOM編碼的識別將由應用程序本身來處理,緣由可從另處一個bug處查看到,由於Unicode對於BOM的編碼的規定可能發生變化。也就是說對於一個UTF-8的文件,應用程序須要知道這個文件有沒有寫BOM,而後本身決定處理BOM的方式。

在上面的while循環中可加入下面的代碼,測試一下讀出內容: 編碼

byte[] allbytes = line.getBytes("UTF-8"); 
			for (int i=0; i < allbytes.length; i++)
			{
				int tmp = allbytes[i];
				String hexString = Integer.toHexString(tmp);
				// 1個byte變成16進制的,只須要2位就能夠表示了,取後面兩位,去掉前面的符號填充
				hexString = hexString.substring(hexString.length() -2);
				System.out.print(hexString.toUpperCase());
				System.out.print(" ");
			}
輸出結果以下:
引用

EF BB BF 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 6C 69 6E 65 2E
?This is the first line.
54 68 69 73 20 69 73 20 73 65 63 6F 6E 64 20 6C 69 6E 65 2E
This is second line.


紅色部分的"EF BB BF"恰好是UTF-8文件的BOM編碼,能夠看出Java在讀文件時沒能正確處理UTF-8文件的BOM編碼,將前3個字節看成文本內容來處理了。

使用連接中提供的代碼能夠解決碰到的亂碼問題:
http://koti.mbnet.fi/akini/java/unicodereader/

修改測試代碼中的輸入流後: spa

Java代碼
  1. BufferedReader br = new BufferedReader(new UnicodeReader(in, Charset.defaultCharset().name())); 

BufferedReader br = new BufferedReader(new UnicodeReader(in, Charset.defaultCharset().name()));


執行,能夠看到正確的結果。

將用到的測試代碼及UTF-8讀取亂碼解決(http://koti.mbnet.fi/akini/java/unicodereader)的源碼放在了附件中 .net

相關文章
相關標籤/搜索