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

相关文章
相关标签/搜索