跨平台UTF-8编码问题
1. 背景
这段时间有需要在Windows和Linux两个系统中进行java的编码。代码先是在Linux平台中进行编写,测试;功能正常之后,迁移到Windows平台。
迁移到Windows平台之后,因为发现有一两个细节需要调整,所以直接在Windows平台上进行改动;但是改动完后的工程无法编译打包,直接提示错误。
我的java工程,使用maven进行依赖的管理,使用eclipse进行代码的开发。Maven中的pom.xml文件定义的工程编码也是UTF-8。
2. 问题
最先发现的问题,是Windows下的eclipse和Linux下的eclipse的默认编码类型不同;Windows平台中的默认编码类型是GB18030,Linux平台的默认编码是UTF-8。此时的错误是“不可映射的字符xxx”;修改方案是把Windows平台下面的java工程的编码格式变更为UTF-8。
因为UTF-8和GB18030本身对字符的表示不同,尤其是中文字符,我们需要对于转换后的源码进行一次检查——如果不进行解决,在进行编译的时候,也有可能出现字符不可识别的问题;如果发现有乱码,则需要重写乱码的这一部分。当然,最简单的就是所有的编码中,不要有中文表示;如果需要注释,也选择英文……
原以为这就是所有的问题了,但是在执行“maven package”进行打包的时候,出现了“xxx.java:[1,0] 需要为class、interface或者enum”的报错提醒,然后打包失败了。对于所有的代码,在IDE中没有任何的错误提醒;甚至将工程重新放在Linux的平台下进行查看,从编辑器中看不出任何问题,但是就是无法编译打包。
此时可以基本确定原因是字符编码的问题,但是无法进行问题排查,因为看不到进一步的信息。不使用IDE的编辑器,使用notepad++和sublime text进行源码文件的查看,终于发现了问题。在sublime text的状态栏中,可以看到有错误的异常。“UTF-8, opened as UTF-8 with BOM (document maybe broken), UTF-8, Unix, Line xx, Column xx”;这里说明现在文件的编码是带有BOM的UTF-8格式,但是该格式可能被破坏了。
3. 原因
BOM(Byte Order Mark),字节序标识,主要用于Unicode系列编码的扩展;但是因为某些原因,java在编译的时候,不识别UTF-8的BOM结构。(JDK Bug 4508058:Java InputStreamReader will support BOM mark for UTF-16 files. But for some reason it does not recognize UTF-8 BOM marks. This is very unfortunate all Windows (>win2k) users if text files are saved with Notepad using UTF-8 format. Notepad will add BOM bytes at the start of file, but Java's InputStreamReader does not skip it.)
UTF-8的BOM,在文件头部附加“EF BB BF”,该字符串序列在编辑器中是不可见的;所以通过一系列的编辑器来查看,是看不到问题的。但是java在进行编译的时候,不会忽略这部分;因此就影响到了正常代码的编译——主要是对于package关键字,无法进行识别了,导致上面的错误。
4. 解决方法
找到问题所在,就比较好办了。最简单的方法,使用notepad++打开有问题的java源代码,然后“格式 -> 以UTF-8无BOM格式编码”,这样保存文件后即可。
想要复杂深入一点,可以写段代码进行文件头的读取和处理;某些web内容就是这么处理的;使用HTTPCLIENT得到的响应内容是UTF-8带BOM的,此时对于整个输入流,读取前三个字节,判断是否为“EF BB BF”,如果是,则截掉。
5. 参考资料
http://koti.mbnet.fi/akini/java/unicodereader/
http://www.unicode.org/faq/utf_bom.html#BOM