HTML5技术

从Java String实例来理解ANSI、Unicode、BMP、UTF等编码概念 - 有梦想的咸鱼(3)

字号+ 作者:H5之家 来源:博客园 2016-01-17 14:14 我要评论( )

main(String[] args) throws UnsupportedEncodingException { [] gbkData = {( byte ) 0xc4, ( byte ) 0xe3, ( byte ) 0xba, ( byte ) 0xc3 }; // 转换到Unicode String tmp = new String(gbkData, "GBK" ); [] big

main(String[] args) throws UnsupportedEncodingException { [] gbkData = {(byte) 0xc4, (byte) 0xe3, (byte) 0xba, (byte) 0xc3}; //转换到Unicode String tmp = new String(gbkData, "GBK"); [] big5Data = tmp.getBytes("Big5"); //后续操作…… }

4.编码丢失问题

  上面已经解释了,JSP框架采用ISO-8859-1字符集来解码的原因。先用一个例子来模拟这个还原过程,代码如下

public class Test { main(String[] args) throws UnsupportedEncodingException { [] data = {(byte) 0xe4, (byte) 0xbd, (byte) 0xa0, (byte) 0xe5, (byte) 0xa5, (byte) 0xbd}; //打印原始数据 showBytes(data); //JSP框架假设它是ISO-8859-1的编码,生成一个String对象 String tmp = new String(data, "ISO-8859-1"); //**************JSP框架部分结束******************** //开发者拿到后打印它发现是6个欧洲字符,而不是预期的"你好" System.out.println(" ISO解码的结果:" + tmp); [] utfData = tmp.getBytes("ISO-8859-1"); //打印还原的数据 showBytes(utfData); //开发者知道它是UTF-8编码的,因此用UTF-8的代码页,重新构造String对象 String result = new String(utfData, "UTF-8"); //再打印,正确了! System.out.println(" UTF-8解码的结果:" + result); } showBytes(byte[] data) { for (byte b : data) System.out.printf("0x%x ", b); System.out.println(); } }

  运行结果如下,第一次输出是不正确的,因为解码规则不对,也查错了代码页,得到的是错误的Unicode。然后发现通过错误的Unicode反查ISO-8859-1代码页还能完美的还原数据。

image

 

  然后我们尝试把ISO-8859-1替换为ASCII,结果就会变成这样子

image

  这是因为,ASCII虽然也是每字节对应一个字符,但是它只对0~127这个空间进行了编码,也就是说每个字节的最大值只能为0x7F,而上面的6个字节全部都大于这个数值,因此在ASCII的代码页中是找不到这6个字节的,于是Java就搞了一个缺省值。我用如下的代码测试发现,当通过编码数据在代码页中查不到对应的Unicode时,就返回缺省值\ufffd(对应图中第一种问号),反过来,当通过Unicode在代码页中查不到对应的编码数据时,就返回缺省值0x3f(ASCII,对应图中第二种问号)。由此,这个输出结果也就可以解释清楚了。

main(String[] args) throws IOException { [] data = {(byte) 0x80}; showUnicode(new String(data, "UTF-8")); showUnicode(new String(data, "GBK")); showUnicode(new String(data, "Big5")); //输出结果全为0x3f String str = "\uccdd"; showBytes(str, "GBK"); showBytes(str, "BIG5"); showBytes(str, "ISO-8859-1"); }

5.Java源文件的编码问题

  这就是开头所提到的那个问题,把问题描述一下先。就如下这么一小段代码,源文件使用UTF-8编码保存。(注意别用Windows的记事本,因为它会在UTF-8文件最前面加入一个3字节的BOM头,而很多程序都不兼容这一点)

public class Test { main(String[] args) { System.out.println("中"); } }

  然后在Windows中使用默认参数编译该文件(系统区域设置为简体中文,即默认使用GBK字符集解码),然后会得到如下错误

  这不是重点,重点如果把“中”换成“中国”,编译就会成功,运行结果如下图。另外进一步可发现,中文字符个数为奇数时编译失败,偶数时通过。这是为什么呢?下面详细分析一下。

  因为Java String内部使用的是Unicode,所以在编译的时候,编译器就会对我们的字符串字面量进行转码,从源文件的编码转换到Unicode(维基百科说用的是与UTF-8稍微有点不同的编码)。编译的时候我们没有指定encoding参数,所以编译器会默认以GBK方式去解码,对UTF-8和GBK有点了解的应该会知道,一般一个中文字符使用UTF-8编码需要3个字节,而GBK只需要2个字节,这就能解释为什么字符数的奇偶性会影响结果,因为如果2个字符,UTF-8编码占6个字节,以GBK方式来解码恰好能解码为3个字符,而如果是1个字符,就会多出一个无法映射的字节,就是图中问号的地方。

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • Java 8 Lambda 表达式 - Felix_ICanFixIt

    Java 8 Lambda 表达式 - Felix_ICanFixIt

    2017-04-22 17:04

  • Omi v1.0.2发布 - 正式支持传递javascript表达式 - 【当耐特】

    Omi v1.0.2发布 - 正式支持传递javascript表达式 - 【当耐特】

    2017-03-22 11:03

  • JavaWeb与Asp.net工作原理比较分析 - 社会主义接班人

    JavaWeb与Asp.net工作原理比较分析 - 社会主义接班人

    2017-03-12 14:00

  • 一道面试题引发的对javascript类型转换的思考 - ChokCoco

    一道面试题引发的对javascript类型转换的思考 - ChokCoco

    2017-03-06 17:00

网友点评
-