Java编码的那些事儿
来上海百度实习也才没多久,这是第三个礼拜吧,接手的项目相当紧迫,而且作为一个实习生我是main coder,不由地带动了极大的工作积极性。这个一开始认为很简单很明了的“小”项目,没想到让我学到了很多东西,今天就讲讲关于Java编码的那些事儿。
Java编码
Unicode是全球标准字符集,是Java所为String采用的编码方式,任何字符用2个字节表示。String实例中保存有一个char[]字符数组。string.getByte()方法可以获得到这个字符串实例在指定编码下的字节数组,注意的是不带参数的getByte方法使用OS默认的字符集,比如GB2312(简体中文)。所以要得到Unicode下的字节数组,需要这样:string.getBytes(“unicode”)(此处注意见下文)。如果使用new String(byte[], Charset)构造,可以将已知编码的字节数组重新拼成一个String实例,即用指定的Charset去组合字节为Unicode字符罢了。同理,不带Charset的String构造使用OS默认字符集。
因此,得到UTF-8的字节数组,按以下步骤:
1 2 3 4 | String str = "梦"; byte[] bytes = str.getBytes("UTF-8"); "使用UTF-8解码字符串得到的UTF-8字节数组" String str2 = new String(bytes, "utf8"); "按照当初被解码的方式(utf8)重新组成Java String类" str.equals(str2) == true; "使用大小写不同的编码写法,来区别不同API中参数代表的意义" |
所以,str = new String(str.getBytes(Charset), Charset) 什么都没有做,除了新建了个String对象。
但是如果你要获取unicode的字节数组,却有非常多的选择,而且很容易出错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void echoBytesTest() { String s = "梦"; echoBytes(s, "Unicode"); echoBytes(s, "UnicodeBig"); echoBytes(s, "UnicodeLittle"); echoBytes(s, "UnicodeBigUnmarked"); echoBytes(s, "UnicodeLittleUnmarked"); echoBytes(s, "UTF-16"); echoBytes(s, "UTF-16BE"); echoBytes(s, "UTF-16LE"); echoBytes(s, "UTF-8"); } void echoBytes(String s, String encoding) { byte[] bytes = s.getBytes(encoding); for (byte b : bytes) { int i = b & 0xff; System.out.print(Integer.toHexString(i) + " "); } System.out.println(); } |
JDK6.0环境下的输出结果如下所示:
Unicode | fe ff 68 a6 | BE顺序,带BOM | 强烈申明不要使用,JDK相关 |
UnicodeBig | fe ff 68 a6 | BE顺序,带BOM | 推荐使用 |
UnicodeLittle | ff fe a6 68 | LE顺序,带BOM | 推荐使用 |
UnicodeBigUnmarked | 68 a6 | BE顺序,无BOM | 推荐使用 |
UnicodeLittleUnmarked | a6 68 | LE顺序,无BOM | 推荐使用 |
UTF-16 | fe ff 68 a6 | BE顺序,带BOM | 不推荐使用,可读性不够高 |
UTF-16BE | 68 a6 | BE顺序,无BOM | 不推荐使用,可读性不够高 |
UTF-16LE | a6 68 | LE顺序,无BOM | 不推荐使用,可读性不够高 |
UTF-8 | e6 a2 a6 | 无BOM | 此为UTF-8字节串,特以此区别 |
然后要特别说明的是,getBytes(“Unicode”)这种方式是什么样的结果,却是与JDK相关的,JDK5.0使用LE顺序,JDK6.0使用BE顺序!
有点复杂,看官方解释。
为了在读取字节时能知道所采用的字节序,在传输时采用了一个名为 “Zero Width No-Break Space” 的字符(中文译名作“零宽无间断间隔”)用于限定字节序,开头两个字节为 FE FF(即-2 -1) 时为 Big-Endian,为 FF FE(即-1 -2) 时为 Little-Endian。 详见 RFC2781 3.2 节。这个就是传说中的BOM头。
UTF-8不需要 BOM 来表明字节顺序,但可以用 BOM 来表明编码方式。字符 “Zero Width No-Break Space” 的 UTF-8 编码是 EF BB BF。
详见:BOM_百度百科
自我理解很像体系结构中的大端编址和小端编址对吧。不知道Unicode为什么要存在Big-Endian和Little-Endian两种顺序,反正就是存在着呗。
说说项目
就是因为string.getBytes(“Unicode”)的JDK相关性,然后我因为使用了旧版本的JDK导致和大家同样从SVN签出的代码的运行结果却完全不一致,由于完全信任Java的平台无关,没有一点怀疑是JDK源码的原因,整整花了一天的时候排查bug。
第二个是Spring MVC的@ResponseBody默认返回的字符集是ISO-8859-1的西文字符,导致返回客户端为乱码,修改Spring配置的见Spring官方论坛的解决方案,不过貌似试了几种方法都没有起效…其实最简单的方式就是放弃使用Spring提供的Ajax方式,直接使用HttpServletResponse的getWriter().write()方法写字符串或getOutputStream.write()方法直接写字节。
参考引用
百度百科:http://baike.baidu.com/view/126558.htm#sub5073178
CSDN论坛:http://topic.csdn.net/u/20081009/09/e899898c-591f-4985-ae88-5972475708fb.html
Spring官方论坛:http://forum.springsource.org/showthread.php?t=81858
这个编码问题我还从来没看这么细过,通常都没有使用Spring来处理response过程,所以很少出现过此种情况
记得PHP页面也是以没有BOM的为好,有BOM的话,也是容易各种问题
好像某些情况下页面开始会出现一个乱码字符
悲剧。。我的暑假也没了~~导师发邮件来了
啊啊啊…要求多久时间呀?暑假没有寝室住啊,得住哪儿啊? :awkward: