posts - 68, comments - 19, trackbacks - 0, articles - 1

java编码问题总结-转自李腾飞老师

Posted on 2011-02-15 16:01 viery 阅读(505) 评论(0)  编辑  收藏 所属分类: JavaSE

电脑只能处理011001这样的二进制数字,字符是日常生活中我们使用的符号,为了电脑能够存储、传输和展示字符,所以,我们需要把字符转换为0110000这样的二进制码。这就是所谓编码。相反,把011000这样的二进制码转换为字符的过程就是解码!JAVA里,char表示一个字符,String表示字符串!

 

具体把哪个字符映射到哪个二进制串上,是由国家(国家标准)、国际组织(国际标准)等决定的!

 

一般不用二进制串来表示某个字符的编码(因为写起来、阅读起来都很麻烦),所以一般是用十六进制的串来表示某个字符的编码。

 

因此:

字符 <--> 十六进制串,之间的映射的集合(因为有很多字符),就构成了字符集。

 

你经常见到的字符集是:Unicode、UTF-8、GB2312、GB18030、GBK、Big-5、ISO-8859-1(又名:Latin-1)

 

Unicode、UTF-8能够支持目前世界上所有语言文字的字符

GB2312是旧的国家标准,不支持繁体汉字

GBK不是国家标准,但支持繁体汉字

GB18030是新的国家标准,也支持繁体汉字

Big-5,能支持繁体汉字

ISO-8859-1,不支持汉字,英美等拉丁语系的国家常用这个编码

 

举例:

public class EncodingTest01 {

    public static void main(String[] args) throws Exception{

       String s = "中国";

       byte[] bs = s.getBytes("GB18030");

       System.out.println(Utils.byteArrayToHex(bs));

    }

}
 
public class Utils {

    private static char[] hexDigits = {

       '0','1','2','3','4','5','6','7',

       '8','9','A','B','C','D','E','F'

    };

    public static String byteArrayToHex(byte[] bs){

       StringBuffer sb = new StringBuffer();

       for(byte b:bs){

           sb.append(hexDigits[(b >> 4) & 0x0000000F]);

           sb.append(hexDigits[b & 0x0000000F]);

           sb.append(",");

       }

       return sb.toString();

    }

}
 

 

“中国”,这两个简体汉字,在不同的字符集中,用上述程序输出,其编码分别为:

Unicode – FE,FF,4E,2D,56,FD,

UTF-8 - E4,B8,AD,E5,9B,BD

GB18030 - D6,D0,B9,FA,

ISO-8859-1 - 3F,3F,

 

解释:

关于Unicode

用Unicode编码之后的“中国”,其编码的前面有FE,FF两个额外的字节。这两个字节称为BOM(Byte Order Mark)。“中”这个汉字,它的Unicode编码是:4E,2D,那么在传输的过程中,是把4E放在前面,还是把2D放在前面呢?这有BOM来决定。如果BOM是FEFF(称为Big Endian),表示4E在前;如果BOM是FFFE(称为Little Endian),表示2D在前。

 

也就是说,如果你的文件编码是Unicode(也可以叫做UTF-16),那么文件开头的字节就是:FEFF(以这种方式开头的编码叫UTF-16BE)或FFFE(以这种方式开头的编码叫做UTF-16LE)

 

UTF-16BE、UTF-16、Unicode是一样的!

 

关于UTF-8

UTF-8编码的文件,其文件头也有一段标识:EF BB BF

 

其它编码的文件,其文件头没有什么标识!!!

 

对于使用ISO-8859-1编码方式来对“中国”二字进行编码之后,得到的是:3F 3F,这是因为ISO-8859-1字符集中根本就没有“中国”这两个字符!所以,它用3F来代替!

 

解码
解码,实际上就是把二进制流(也就是字节流,即字节数组)转换为字符。字节数组是什么样的编码(即用哪个字符集对它编码),你必需使用相同的字符集来解码!

 

public class EncodingTest02 {

    public static void main(String[] args) throws Exception{

       byte[] bs = {(byte)0xD6,(byte)0xD0,(byte)0xB9,(byte)0xFA};

       String s = new String(bs,"GB18030");

       System.out.println(s);

    }

}
 

 

像上述程序所示的那样,我们知道:D6D0 B9FA 是“中国”两个字符的“GB18030”编码,所以,你可以使用这个字符集来把其对应的字节数组解码为字符!

 

Java中的字符
你首先要理解的是,JAVA可以把字符存储在内存中(即也是以0110010这样的方式存储在内存中)!那么,JAVA把字符存储在内存中的时候,它使用的是什么编码呢?答案是:Unicode

 

因此,我们知道,“中国”,这两个汉字,在内存中,是以“4E,2D,56,FD”这种方式存在的!

 

不过要注意,JAVA的class文件,是以UTF-8方式编码的!JAVA虚拟机读取class文件的时候,通过UTF-8编码把class文件读入内存,并转换为UTF-16(即Unicode)。

 

因此,new String(byte[],“GB18030“)的真正意思是:把字节流(它是以GB18030方式编码的)转换为Unicode的字节流存在内存中!

 


 

如上图所示,假设有“中国”两个汉字,通过getBytes(“encoding”)能将其转换为不同的编码!

 

再看下图,假设有一个字节流,现在想把它转换为字符串:

 

 

 

一个本来是UTF-8编码的字节流,如果你把它当成ISO-8859-1进行转换,ISO-8859-1的规则是每个字节一个字符,所以得到了长度为6的字符串,如果你将它输出,将是乱码!现在你可以反向转换,通过ISO-8859-1编码得到它的字节流,这将原封不动得到一个字节流,然后,你又可以对这个字节流进行正确的解码了!

 

简单总结一下
如果你拿到一个字节流,你需要清楚的知道,你这个字节流是用哪个字符集进行编码的!

 

如果你拿到一个字符串,你发现它输出的时候是乱码,那么,你究竟能不能通过某种技术手段解决这个乱码问题呢?答案是:不一定!

l           因为你拿到了一个字符串,所以,可以肯定,你拿到的字符串是由于某人将它按照某种字符集将字节流进行了解码而得到的字符串!

a)         假如某人是按照ISO-8859-1字符集对字节流进行解码而得到的字符串,那么,恭喜你,你将可以通过某种技术手段,把你手中的乱码字符串转换为正确的字符串!这种技术手段就是:先getBytes(“ISO-8859-1”)得到一个字节流,然后把这个字节流用new String(byte[],“正确的字符集”)转换为正确的字符串即可!

b)        假如某人不是按照ISO-8859-1字符集对字节流进行解码的,那么,很不幸,有很大的可能是你没有办法将它再正确地还原了!

 

页面编码
通过GET方法向后台递交的请求
向服务器递交的请求中,字符也是需要进行编码之后传输的!浏览器将会自动对URL地址中的汉字进行编码之后传输。如果你直接在浏览器地址栏输入URL地址及汉字,那么浏览器将自动使用当前操作系统的默认字符集进行编码(中文操作系统就是GB2312)之后传输到服务器;如果你在某个页面上点击一个链接(此链接后面附带着中文汉字),那么,浏览器将自动根据这个页面所使用的字符集来对汉字进行编码之后,传输到服务器!

 

比如“中国”两个汉字,假如页面编码是GB18030(或GBK或GB2312),那么向后传输的串就是:%D6%D0%B9%FA

 

比如:http://localhost:8080/myservlet/AddUser?username=%D6%D0%B9%FA

 

我们后台使用request.getParameter(“username”)来得到一个字符串,结果发现是乱码,那么可以肯定,某人帮我们将从客户端传输过来的字节流按照某种字符集解码成了字符串!某人就是应用服务器(TOMCAT),某种字符集就是应用服务器的默认字符集,是ISO-8859-1。所以,很幸运,还有救!

       String username = request.getParameter("username");

      

       //将它转换为正确的字符串!

       byte[] bs = username.getBytes("ISO-8859-1");

       username = new String(bs,"GB18030");

      

       //正确

       System.out.println(username);
 

 

当然,为了避免每次都这样干,我们干脆修改TOMCAT对于URL编码的默认解码字符集即可!

修改server.xml文件中的下面的语句,加上URIEncoding=“GB18030“的配置即可:

    <Connector port="8080" protocol="HTTP/1.1"

               connectionTimeout="20000"

               redirectPort="8443" URIEncoding="GB18030"/>
 

 

通过POST向后台递交的请求
浏览器将按照表单所在的页面所使用的字符集对数据进行编码之后传输!

 

在TOMCAT中配置的URIEncoding只对GET方式递交的请求中的数据有效,而对于POST请求,你必须在调用request.getParameter(“xxx”)之前,调用:request.setCharacterEncoding("GB18030"); !

 

Response的编码
 

在调用response.getWriter()之前,调用response.setCharacterEncoding("GB18030");

即可!

 

或者:

在设置ContentType的时候设置也可以:

response.setContentType("text/html;charset=GB18030");

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/li_tengfei/archive/2010/12/25/6098099.aspx


只有注册用户登录后才能发表评论。


网站导航: