关于中文编码问题及Java中的处理

来源:互联网 发布:神奇百货 知乎 编辑:程序博客网 时间:2024/05/18 22:13
       一直以来,就有个不大正确的观念,也导致中文问题困扰了我很久,所谓错误观念就是“中文占2个字节,英文数字占1个字节”。现在明白,其实这样子说法是有条件的。
        其中参考了几篇文章,以下也只是对文章的总结和笔记。
        《从 汉化 到 国际化》
        《深入剖析JSP和Servlet对中文的处理》
        《Java中文问题详解》
    
          关于字符编码
          具体可以参见:http://yeyongq.spaces.live.com/Blog/cns!A52629657552983C!1728.entry?owner=1
          其实在网上关于字符编码的文章很多,其实各个常用字符集是如下关系:
          gb2312是一个小的中文集,大概支持6000多个字,中文为2字节;
          gbk是gb2312扩展,基本涵盖了大部分的中文字,中文为2字节;
          两个针对全人类所有字符的编码字符集,一个是UCS(Universal Character Set,它是国际标准化组织弄出来的,也就是ISO/IEC 10646;另一个是Unicode(Universal Code,Unicode同时也是一个联盟的名字,也就是HP、Microsoft、IBM、Apple等几家知名的大型计算企业所组成的联盟集团,他们为了推进多种文种的统一编码而制定出了Unicode。
           UTF-8则是为了解决Unix下使用Unicode而设计的,它是在 Unix 风格的操作系统下使用 Unicode 的明显的方法,中文为3字节。(http://www.linuxforum.net/books/UTF-8-Unicode.html)
       
           Java中String和Char的区别
            在Java中支持Unicode,因此一个char占了2 bytes,也即16bit。因此可以放得下一个中文字:
            char a = '你';
            char b = 'y';
             都是合法的。
           而String.getBytes()方法,则是要根据系统默认的字符集编码返回bytes,为了有更好的实用性,最好还是使用.getBytes("字符集")的格式。对于String c = "你"的情况,GBK会返回2个byte,UTF-8会返回3个byte.
         
           操作系统对JVM编码的影响
           《Java的中文处理学习笔记》中讲到的比较全面,可以归纳为:JVM的默认字符编码集与操作系统设置有关。在System.getProperties中的file.encoding就是根据操作系统的设置而变化的。
          在Linux下
LANG=zh_CN; LC_ALL=zh_CN.GBK; export LANG LC_ALL 设置。(还未测试过)
          在Windows下,使用区域设置。

          javac -encoding的影响
          编译器就是根据这个变量来读取java文件的,然后把用UTF-8形式编译成class文件。 也就是说-encoding决定的只是如何读取源文件,而class总是以UTF-8格式编译的。
 
          JSP/Servlet中文的影响
          可以参见《深入剖析JSP和Servlet对中文的处理》,其中提到了3个概念:
          Complier-charset   就是指的javac -encoding的字符集
          Servlet-charset, 指的是HttpServletResponse.setContentType(),即返回时指定浏览器使用的字符集
          Jsp-charset,只用于jsp页面上,<%@ page contentType="text/html; charset=utf-8" %>,对于指定了这个属性的jsp文件,Complier-charset 和Servlet-charset都被指定为jsp-charset,上例:指定两者的都是utf-8。
          文章中提到对于Complier和Servlet两者指定不同的charset会有乱码的情况,我认为指的是Servlet里有中文的情况才会出现,处理中文是不会有乱码的。因为编译器用A编码读取java文件,编译成UTF-8格式的class,运行时,如果返回给用户浏览器指定的是B编码,当然会有乱码的情况。
         我理解是,用了jsp-charset,然后保证文本文件的编码和其相同,就可以保证页面上静态的中文会没有问题。我们经常会看到ISO8859-1,是因为英文操作系统中,不指定编码时,默认是此编码

          浏览器对字符集编码的影响
          IE中,对于中文参数的发送,都是将其转换为UTF格式,并送入传输流中,而Servlet会先用readUTF读取传输流。针对中文参数造成的乱码以及参数中有特殊字符,可以考虑用java.netURLEncoder.encode(String,String),指定编码方式后,再传输。如果在Servlet中,可以通过request.setCharacterEncoding,来指定UTF读取后用何种方式解码。
        转载一下:自己认为对这个过程描述得比较清晰的文章,《JAVA字符集》里提到:
rlrb;A
“3.4.1. 表单输入  
 
User input *(gbk:d6d0 cec4) browser *(gbk:d6d0 cec4) web server iso8859-1(00d6 00d 000ce 00c4) class,需要在class中进行处理:getbytes("iso8859-1")为d6 d0 ce c4,new String("gbk")为d6d0 cec4,内存中以unicode编码则为4e2d 6587。  
 
l 用户输入的编码方式和页面指定的编码有关,也和用户的操作系统有关,所以是不确定的,上例以gbk为例。  
 
l 从browser到web server,可以在表单中指定提交内容时使用的字符集,否则会使用页面指定的编码。而如果在url中直接用?的方式输入参数,则其编码往往是操作系统本身的编码,因为这时和页面无关。上述仍旧以gbk编码为例。  
 
l Web server接收到的是字节流,默认时(getParameter)会以iso8859-1编码处理之,结果是不正确的,所以需要进行处理。但如果预先设 置了编码(通过request. setCharacterEncoding ()),则能够直接获取到正确的结果。  
 
l 在页面中指定编码是个好习惯,否则可能失去控制,无法指定正确的编码。  
 
3.4.2. 文件编译  
 
假设文件是gbk编码保存的,而编译有两种编码选择:gbk或者iso8859-1,前者是中文windows的默认编码,后者是linux的默认编码,当然也可以在编译时指定编码。  
 
Jsp *(gbk:d6d0 cec4) java file *(gbk:d6d0 cec4) compiler read uincode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4) compiler write utf(gbk: e4b8ad e69687; iso8859-1: *) compiled file unicode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4) class。所以用gbk编码保存,而用iso8859-1编译的结果是不正确的。  
 
class unicode(4e2d 6587) system.out / jsp.out gbk(d6d0 cec4) os console / browser。  
 
l 文件可以以多种编码方式保存,中文windows下,默认为ansi/gbk。  
 
l 编译器读取文件时,需要得到文件的编码,如果未指定,则使用系统默认编码。一般class文件,是以系统默认编码保存的,所以编译不会出问题,但对于 jsp文件,如果在中文windows下编辑保存,而部署在英文linux下运行/编译,则会出现问题。所以需要在jsp文件中用 pageEncoding指定编码。  
 
l Java编译的时候会转换成统一的unicode编码处理,最后保存的时候再转换为utf编码。  
 
l 当系统输出字符的时候,会按指定编码输出,对于中文windows下,System.out将使用gbk编码,而对于response(浏览器),则使用 jsp文件头指定的contentType,或者可以直接为response指定编码。同时,会告诉browser网页的编码。如果未指定,则会使用 iso8859-1编码。对于中文,应该为browser指定输出字符串的编码。  
 
l browser显示网页的时候,首先使用response中指定的编码(jsp文件头指定的contentType最终也反映在response上),如果未指定,则会使用网页中meta项指定中的contentType。  ”

总结一下,就是浏览器这端和操作系统和页面指定编码有关,发送时,是可以指定为以UTF-8发送的,服务器接受时,例如Tomcat,默认会以ISO_8859处理,这是英文操作系统默认的编码方式。

Tomcat5.0与4.0对编码的影响
参考《Tomcat和STRUTS中的中文问题》,简单来说,就是4.0,可以用request.setCharacterEncoding在getParameter之前指定编码来解决。
但是,5.0以上,包括6.0,因为区分了GET和POST方法的处理,POST方法仍可以用setCharacterEncoding,但是GET方法则需要设定server.xml文件中URIEncoding参数,或者在程序中使用String.getBytes("ISO-8859-1")。

Weblogic对编码的影响
与Tomcat类似,通过设置setCharacterEncoding能解决大部分问题,而weblogic9默认的编码也是ISO-8859-1,如果要在URL中传输中文出现乱码,可以尝试String.getBytes("ISO-8859-1")

总结
这是一个比较大的题目,网上讨论的也很多,我看了之后,觉得比较实用的是以下几篇:
Java的中文处理学习笔记
JAVA字符集
Tomcat和STRUTS中的中文问题
Java/J2EE中文问题解决办法
深入剖析JSP和Servlet对中文的处理

有兴趣的可以分别看看,总结一下,较好的JAVA Web解决方法,应该是《Java/J2EE中文问题解决》中提到的用UTF-8结扎。指的是一下步骤:
1.确定编辑器使用的是UTF-8编码,保存文件
2.设定jsp-charset为UTF-8,这样就保证了源文件编译和服务器返回的编码都是UTF-8
3.如果遇到中文传送URL的话,应该先使用java.net.URLEncoding方法先编码再传输,接受时,如果设置了第1步,一般就不会乱码,如果真的遇上Tomcat5.0,那就再使用设置server.xml的URIEncoding属性,或者使用getBytes("ISO-8859-1"),因为一般的英文系统包括JAVA EE容器默认都是已这种格式处理字符。
F8e1E1"b ,pOmH2N5u ",*f$~qK "1U9-gG ;n il`;Pg tz& @dX e}$_x0DQ 3 /5<a,K* 1At4}/L ]!Nzu~pk' d.oA=A)  ]/ Q.HVikJw aw5 *6y Q$Ipu=w! w @y< #FYp_W`i ttUJpr| z$AXf`:.B U$73=HH} dYTV5H>A ]};uGzp8 ZG^.'Z ?39aW4%g 6 _^X@$IVD sBrpJc 'KxUv$5x vtCd6cl=c