最近在处理文件时发现了同样类型的文件使用的编码可能是不同的。所以想将文件的格式统一一下(因为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格式保存,则在使用程序读取是会从文件中多读出一个不可见字符。
测试代码如下:
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编码,其内容为:
正常的测试结果应该是直接输出utf.txt的文本内容。可是实际上却输出了下面的内容:
第一行多出了一个问号。
通过上面的几篇文章应该可以想到是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"刚好是UTF-8文件的BOM编码,可以看出Java在读文件时没能正确处理UTF-8文件的BOM编码,将前3个字节当作文本内容来处理了。
使用链接中提供的代码可以解决碰到的乱码问题:
http://koti.mbnet.fi/akini/java/unicodereader/
修改测试代码中的输入流后:
BufferedReader br = new BufferedReader(new UnicodeReader(in, Charset.defaultCharset().name()));
执行,可以看到正确的结果。
将用到的测试代码及UTF-8读取乱码解决(http://koti.mbnet.fi/akini/java/unicodereader)的源码放在了附件中
累了,去 淘宝皇冠店铺 看看