HTML5技术

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

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

转换方法说起来比较容易,对于UTF系列或者是ISO-8859-1这种被兼容的编码,可以通过计算和Unicode数值直接进行转换(实际可能也是查表),而对于系统遗留下来的ANSI编码,则只能通过查表的方式进行,微软把这种映射

  转换方法说起来比较容易,对于UTF系列或者是ISO-8859-1这种被兼容的编码,可以通过计算和Unicode数值直接进行转换(实际可能也是查表),而对于系统遗留下来的ANSI编码,则只能通过查表的方式进行,微软把这种映射表称为Code Page(代码页),并按编码进行分类编号,比如我们常见的cp936就是GBK的代码页,cp65001就是UTF-8的代码页。下图是微软官网查到的GBK->Unicode映射表(目测不全),同理还应有反向的Unicode->GBK映射表。

5XSYJX8ZCHRDRUL0OS18(}N

  有了代码页,就可以很方便的进行各种编码转换了,比如从GBK转换到UTF-8,只需要先按照GBK的编码规则对数据按字符划分,用每个字符的编码数据去查GBK代码页,得到其Unicode数值,再用该Unicode去查UTF-8的代码页(或直接计算),就可以得到对应的UTF-8编码。反过来同理。注意:UTF-8是Unicode的标准实现,它的代码页中包含了所有的Unicode取值,所以任意编码转换到UTF-8,再转换回去都不会有任何丢失。至此,我们可以得出一个结论就是,要完成编码转换工作,最重要的是第一步要成功的转换到Unicode,所以正确选择字符集(代码页)是关键。

  理解了转码丢失问题的本质后,我才突然明白JSP的框架为什么要以ISO-8859-1去解码HTTP请求参数,导致我们获取中文参数的时候不得不写这样的语句:

String param = new String(s.getBytes("iso-8859-1"), "UTF-8");

  因为JSP框架接收到的是参数编码的二进制字节流,它不知道这究竟是什么编码(或者不关心),也就不知道该查哪个代码页去转换到Unicode。然后它就选择了一种绝对不会产生丢失的方案,它假设这是ISO-8859-1编码的数据,然后查ISO-8859-1的代码页,得到Unicode序列,因为ISO-8859-1是按字节编码的,而且不同于ASCII的是,它对0 ~ 255空间的每一位都进行了编码,所以任意一个字节都能在它的代码页中找到对应的Unicode,若再从Unicode转回原始字节流的话也就不会有任何丢失。它这样做,对于不考虑其他语言的欧美程序员来说,可以直接用JSP框架解码好的String,而要兼容其他语言的话也只需要转回原始字节流,再以实际的代码页去解码一下就好。

  我对Unicode以及字符编码的相关概念阐述完毕,接下来用Java实例来感受一下。

三、实例分析 1.转换到Unicode——String构造方法

public class Test { main(String[] args) throws IOException { [] gbkData = {(byte)0xc4, (byte)0xe3, (byte)0xba, (byte)0xc3}; [] big5Data = {(byte)0xa7, (byte)0x41, (byte)0xa6, (byte)0x6e}; //构造String,解码为Unicode String strFromGBK = new String(gbkData, "GBK"); String strFromBig5 = new String(big5Data, "BIG5"); //分别输出Unicode序列 showUnicode(strFromGBK); showUnicode(strFromBig5); } showUnicode(String str) { for (int i = 0; i < str.length(); i++) { System.out.printf("\\u%x", (int)str.charAt(i)); } System.out.println(); } }

  运行结果如下图

  

  从结果可以发现,只要指定了正确的字符集(代码页),String就可以解码出正确的Unicode,最后可以试试println("\u4f60\u597d"),输出的就是“你好”。

2.Unicode转换到各种编码——getBytes

  String拥有了Unicode序列,想要转换到其它编码就易如反掌了,根据你参数指定的字符集,去相应的代码页查找就可以转换过去了,当然如果该字符集不支持某字符(也就是没有这条Unicode记录),那就会导致编码丢失,再也不能还原到原来的Unicode序列了。

  这里,我们和第1节的做法相反,我们把Unicode序列转换到其它各种编码,如下所示。

public class Test { main(String[] args) throws IOException { //字符串"你好" String str = "\u4f60\u597d"; //转换到各种编码 showBytes(str, "GBK"); showBytes(str, "BIG5"); showBytes(str, "UTF-8"); } showBytes(String str, String charset) throws IOException { for (byte b : str.getBytes(charset)) System.out.printf("0x%x ", b); System.out.println(); } }

  运行结果如下图

  可以发现,由于String掌握了Unicode码,要转换到其它编码so easy!

3.以Unicode为桥梁,实现编码互转

  有了上面两部分的基础,要实现编码互转就很简单了,只需要把他们联合使用就可以了。先new String把原编码数据转换为Unicode序列,再调用getBytes转到指定的编码就OK。

  比如一个很简单的GBK到Big5的转换代码如下

 

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

网友点评
c