前几天在使用中兴的MM7接口开发一个彩信的应用,可是在测试的时候,手机收到的文本内容总是一堆乱码,不管怎么修改参数问题都得不到解决,于是就狠狠的琢磨和研究了下关于编码的内容,最终成功解决了乱码的问题。


我们都知道Java语言使用的是Unicode编码。可是大家是否真的已经理解了这句话的含义? Unicode编码和我们常用的UTF-8,GBK有什么关系呢? 那接下来就来讨论下这个话题。


不知大家还有没想过,我们在Java代码里面定义一个字符串字面量的时候,该字符串在JVM中的编码是什么?  是的,上面也说了Java语言用的是Unicode编码,所以这样定义的字符串字面量自然就是Unicode编码表示的。 然而除了这种程序内部的数据外,我们经常还会从外部获取各种各样的数据让程序处理,例如通过IO从硬盘上读取文件,通过JDBC查询数据库等,而这些数据的编码格式是多种多样的,例如有的可能是UTF-8,有的可能是GBK等,那么这样的一些数据在JVM中又是以什么编码表示的呢?


上面提到了几个问题,现在返回去简单的说说编码,当然这里只是粗略的提及,毕竟网上这方面的内容很多。


我们首先要弄清楚两个概念,“字符集编码”和“编码格式”。所谓“字符集编码”就是由某个组织制定的一张“字符与编号的映射关系表”,例如: 10001 == “我”, 10002 = “们”。 我们所谓的Unicode字符集就是这样的一张关系表。


而UTF-8是一种实现了Unicode字符集中部分字符编码的“编码格式”,它存在目的是为了保存或者传输数据用的。那么既然在字符集中已经有了映射关系了,为什么还要再来编码。原因好几个,例如考虑文件大小的问题,使用效率的问题,和其他字符集区分的问题等。


那么UTF-8编码又是对Unicode字符集中的哪部分编码呢? 就是“字符编号”,例如上面的10001,10002.  而对于UTF-8的编码规则可以网上查阅.


那现在我们就应该清楚了,在JVM中表示字符串“我们”使用的编码为“1000110002”这样的格式,也就是使用的是Unicode的编号。那当我们通过IO将一个以UTF-8编码的文本文件读入内存的时候,很可能我们会使用到这样的代码:


new InputStreamReader(new FileInputStream(file),"UTF-8")


这里指定的“UTF-8”就是文件的编码格式,它的目的是告诉负责解码的对象要按照“UTF-8”的编码格式来解码成JVM使用的Unicode编码。那如果我们这里指定为“GBK”的话,我们所得到的可就是一堆乱码啦。


如果String content = (文件内容) 。 那么这个content在这个时候已经是Unicode编码了。不知道现在为止是否应该明白了些什么。


如果你看到这样的代码:
String msg  = "我是一个字符串" ;
String res = new String(msg.getBytes("utf-8"),"GBK") ;

那这里的res打印出来也就是一堆乱码了。这里的操作过程其实是:
1,将Unicode编码的msg按照UTF-8的编码格式进行编码,从而得到UTF-8编码的字节数组。
2,将字节数组按照GBK的编码格式来解码成Unicode编码的字符
3,可惜UTF-8和GBK不兼容,他们使用了不同的编码集,所以乱码出来啦


在进行http请求的时候,我们需要告诉对方我们发送的数据的编码格式是什么,如果对方按照我们的告知的编码格式来解码,而我们却把错误的编码格式告诉了对方,那么接下来的事情就是对方收到了一堆的乱码。


还有就是我们在进行这样的操作的时候:"我是一个字符串".getBytes(); 可要小心了,这个时候得到的字节数组可是你操作系统默认的编码格式(当然我们还可以在启动的时候指定我们默认的编码格式)。


下面再追加点编码解码的例子,供大家理会:

//定义一个Unicode字符类型的字符串
  String msg = "我是一个字符串哦,还是Unicode编码的。體發財"  ;
  System.out.println("原有字符串:"+msg);
  System.out.println("=============");
  //将Unicode编码按照GBK编码格式进行编码
  byte[] gbkbyts = msg.getBytes("GBK");
  
  /*
   * 本来是GBK编码的字节,我们使用UTF-8来解码.当然此时必然会是乱码。
   * 
   * 注意:如果在第一次解码的时候使用这种错误的解码方式,
   * 那么后面将无法再恢复,下面的例子可以看到。
   * */
  String utf8Str = new String(gbkbyts,"UTF-8") ;
  System.out.println("UTF-8解码后的字符串:"+utf8Str);
  /*
   * 接下来我们把该字符串按照UTF-8的编码格式再编码回去.
   * 但这个时候gbkbyts和utf8Byts已经完全不同了。
   * */
  byte[] utf8Byts = utf8Str.getBytes("UTF-8") ;
  /*
   * 接着我们再来按照GBK的编码格式来解码.
   * 得到的自然是乱码。
   * */
  String utf2GbkStr = new String(utf8Byts,"GBK") ;
  System.out.println("GBK解码UTF-8编码后的字符串:"+utf2GbkStr);
  System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
  /*
   * 但是,如果我们第一次解码的时候使用的是单字节的编码格式来解码,那后面是可以再恢复的。
   * 可是还有一个要求,就是要保证该单字节的编码会利用上所有的8位,
   * 也就是说可以表示256个字符的才行。
   * 
   * 
   *单字节的编码格式有两种, ASCII和ISO-8859-?
   *ASCII使用了7位来表示字符,最高位永远为0,其可以表示的范围只有128个字符。
   *ISO-8859-?(问号表示1,2,3…11,13…16,没有12,表示15个字符集)
   *使用8位来表示字符,总共可以表示256个字符。
   *
   *这两种单字节编码,我们最常用的也就是ISO-8859-1
   * */
  //转换为ASCII码的字符串
  String ascStr = new String(gbkbyts,"ASCII") ;
  System.out.println("ASCII解码后的字符串:"+ascStr);
  //再将ASCII转换为GBK
  String gbkStr = new String(ascStr.getBytes("ASCII"),"GBK") ;
  System.out.println("将ASCII编码按照GBK编码格式解码:"+gbkStr);
  System.out.println("===============================");
  
  
  String isoStr = new String(gbkbyts,"ISO-8859-1") ;
  System.out.println("ISO-8859-1解码后的字符串:"+ascStr);
  //再将ASCII转换为GBK
  String gbkStr1 = new String(isoStr.getBytes("ISO-8859-1"),"GBK") ;
  System.out.println("将ISO-8859-1编码按照GBK编码格式解码:"+gbkStr1);

其运行结果如下:

原有字符串:我是一个字符串哦,还是Unicode编码的。體發財
=============
UTF-8解码后的字符串:???????????????????Unicode???????w?l?
GBK解码UTF-8编码后的字符串:锟斤拷锟斤拷一锟斤拷锟街凤拷锟斤拷哦锟斤拷锟斤拷锟斤拷Unicode锟斤拷锟斤拷摹锟斤拷w锟絣財
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ASCII解码后的字符串:??????????????????????Unicode?????????w?l??
将ASCII编码按照GBK编码格式解码:??????????????????????Unicode?????????w?l??
===============================
ISO-8859-1解码后的字符串:??????????????????????Unicode?????????w?l??
将ISO-8859-1编码按照GBK编码格式解码:我是一个字符串哦,还是Unicode编码的。體發財


以上的内容大概的讲了下关于java中编码的问题,也算是一个抛砖引玉的过程,欢迎指正。