3.  中文转码时''?''、乱码的由来 两个方向转换都有可能得到错误的结果: Unicode-->Byte,  如果目标代码集不存在对应的代码,则得到的结果是0x3f. 
如: 
"\u00d6\u00ec\u00e9\u0046\u00bb\u00f9".getBytes("GBK") 腔赋别岆"?足谷F?迄", Hex 硉岆3fa8aca8a6463fa8b4. 
仔细看一下上面的结果,你会发现\u00ec被转换为0xa8ac, \u00e9被转换为\xa8a6... 它的实际有效位变长了! 这是因为GB2312符号区中的一些符号被映射到一些公共的符号编码,由于这些符号出现在ISO-8859-1或其它一些SBCS字符集中,故它们在Unicode中编码比较靠前,有一些其有效位只有8位,和汉字的编码重叠(其实这种映射只是编码的映射,在显示时仔细不是一样的。Unicode 中的符号是单字节宽,汉字中的符号是双字节宽) . 在Unicode\u00a0--\u00ff 之间这样的符号有20个。了解这个特征非常重要!由此就不难理解为什么JAVA编程中,汉字编码的错误结果中常常会出现一些乱码(其实是符号字符), 而不全是''?''字符, 就比如上面的例子。 Byte-->Unicode,  如果Byte标识的字符在源代码集不存在,则得到的结果是0xfffd. 
如: 
Byte ba[] = {(byte)0x81,(byte)0x40,(byte)0xb0,(byte)0xa1};new String(ba,"gb2312"); 
结果是"?啊", hex 值是"\ufffd\u554a". 0x8140 是GBK字符,按GB2312转换表没有对应的值,取\ufffd. (请注意:在显示该uniCode时,因为没有对应的本地字符,所以也适用上一种情况,显示为一个"?".) 实际编程中,JSP/Servlet 程序得到错误的汉字信息,往往是这两个过程的叠加,有时甚至是两个过程叠加后反复作用的结果. 4. JSP/Servlet 汉字编码问题及在 WAS 中的解决办法 4.1 常见的 encoding 问题的现象 
网上常出现的 JSP/Servlet encoding 问题一般都表现在 browser 或应用程序端,如: 
浏览器中看到的 Jsp/Servlet 页面中的汉字怎么都成了 ’?’ ? 
浏览器中看到的 Servlet 页面中的汉字怎么都成了乱码? 
JAVA 应用程序界面中的汉字怎么都成了方块? 
Jsp/Servlet 页面无法显示 GBK 汉字。 
JSP 页面中内嵌在<%...%>,<%=...%>等Tag包含的 JAVA code 中的中文成了乱码,但页面的其它汉字是对的。 
Jsp/Servlet 不能接收 form 提交的汉字。 
JSP/Servlet 数据库读写无法获得正确的内容。 
隐藏在这些问题后面的是各种错误的字符转换和处理(除第3个外,是因为 Java font 设置错误引起的)。解决类似的字符 encoding 问题,需要了解 Jsp/Servlet 的运行过程,检查可能出现问题的各个点。 

解决方案 »

  1.   

    4.2 JSP/Servlet web 编程时的 encoding 问题 
    运行于Java 应用服务器的 JSP/Servlet 为 Browser 提供 HTML 内容,其过程如下图所示: 
    其中有字符编码转换的地方有: JSP 编译。Java 应用服务器将根据 JVM 的 file.encoding 值读取 JSP 源文件,编译生成 JAVA 源文件,再根据 file.encoding 值写回文件系统。如果当前系统语言支持 GBK,那么这时候不会出现 encoding 问题。如果是英文的系统,如 LANG 是 en_US 的 Linux, AIX 或 Solaris,则要将 JVM 的 file.encoding 值置成 GBK 。系统语言如果是 GB2312,则根据需要,确定要不要设置 file.encoding,将 file.encoding 设为 GBK 可以解决潜在的 GBK 字符乱码问题 
    Java 需要被编译为 .class 才能在 JVM 中执行,这个过程存在与a.同样的 file.encoding 问题。从这里开始 servlet 和 jsp 的运行就类似了,只不过 Servlet 的编译不是自动进行的。对于JSP程序, 对产生的JAVA 中间文件的编译是自动进行的(在程序中直接调用sun.tools.javac.Main类). 因此如果在这一步出现问题的话, 也要检查encoding和OS的语言环境,或者将内嵌在JSP JAVA Code 中的静态汉字转为 Unicode, 要么静态文本输出不要放在 JAVA code 中。 对于Servlet, javac 编译时手工指定-encoding 参数就可以了。 
    Servlet 需要将 HTML 页面内容转换为 browser 可接受的 encoding 内容发送出去。依赖于各 JAVA App Server 的实现方式,有的将查询 Browser 的 accept-charset 和 accept-language 参数或以其它猜的方式确定 encoding 值,有的则不管。因此采用固定encoding 也许是最好的解决方法。对于中文网页,可在 JSP 或 Servlet 中设置 contentType="text/html;charset=GB2312";如果页面中有GBK字符,则设置为contentType="text/html;charset=GBK",由于IE 和 Netscape对GBK的支持程度不一样,作这种设置时需要测试一下。 
    因为16位 JAVA char在网络传送时高8位会被丢弃,也为了确保Servlet页面中的汉字(包括内嵌的和servlet运行过程中得到的)是期望的内码,可以用 PrintWriter out=res.getWriter() 取代 ServletOutputStream out=res.getOutputStream(). PrinterWriter 将根据contentType中指定的charset作转换 (ContentType需在此之前指定!);也可以用OutputStreamWriter封装 ServletOutputStream 类并用write(String)输出汉字字符串。 
    对于 JSP,JAVA Application Server 应当能够确保在这个阶段将嵌入的汉字正确传送出去。 
    这是解释 URL 字符 encoding 问题。如果通过 get/post 方式从 browser 返回的参数值中包含汉字信息, servlet 将无法得到正确的值。SUN的 J2SDK 中,HttpUtils.parseName 在解析参数时根本没有考虑 browser 的语言设置,而是将得到的值按 byte 方式解析。这是网上讨论得最多的 encoding 问题。因为这是设计缺陷,只能以 bin 方式重新解析得到的字符串;或者以 hack HttpUtils 类的方式解决。参考文章 2 均有介绍,不过最好将其中的中文 encoding GB2312、 CP1381 都改为 GBK,否则遇到 GBK 汉字时,还是会有问题。 
    Servlet API 2.3 提供一个新的函数 HttpServeletRequest.setCharacterEncoding 用于在调用 request.getParameter(“param_name”) 前指定应用程序希望的 encoding,这将有助于彻底解决这个问题。 
    4.3 IBM Websphere Application Server 中的解决方法 WebSphere Application Server 对标准的 Servlet API 2.x 作了扩展,提供较好的多语言支持。运行在中文的操作系统中,可以不作任何设置就可以很好地处理汉字。下面的说明只是对WAS是运行在英文的系统中,或者需要有GBK支持时有效。 奻扴c,d?表ㄛWAS 饮猁娠戙 Browser 腔逄晟扢离ㄛ婓?吽袨表狟ㄛ zh, zh-cn 胀歙掩茬捍峈 JAVA encoding CP1381ㄗ蛁砩ㄩ CP1381 硐岆胀肮衾 GB2312 腔珨跺 codepageㄛ膻衄 GBK 盓厥ㄘ〔涴欴酕扂砑岆秪峈拸杨?? Browser 堍俴腔绂釬炵苀岆盓厥GB2312, 逊岆 GBKㄛ垀眕??苤〔笋岆妗匿腔茼蚚炵苀逊岆猁?珜泼笢堤珋 GBK 荦趼ㄛ郔翍靡腔岆紾軞烩靡趼笢腔※噷"(rong2 ㄛ0xe946ㄛ\u9555)ㄛ垀眕衄奀逊岆剒猁蔚 Encoding/Charset 硌隅峈 GBK〔绞? WAS 笢曹载?吽腔 encoding 膻衄奻泼次腔饶系镑岁ㄛ渀勤 a,bㄛ统蕉恅梒 5ㄛ婓 Application Server 腔韬锷俴统杅笢硌隅 -Dfile.encoding=GBK 捞褫· 渀勤 dㄛ婓 Application Server 腔韬锷俴统杅笢硌隅-Ddefault.client.encoding=GBK〔?别硌隅賸-Ddefault.client.encoding=GBKㄛ饶系c?表狟褫眕祥淫硌隅charset〔 上面列出的问题中还有一个关于Tag<%...%>,<%=...%>中的 JAVA 代码里包含的静态文本未能正确显示的问题,在WAS中的解决方法是除了设置正确的file.encoding, 还需要以相同方法设置-Duser.language=zh -Duser.region=CN。这与JAVA locale的设置有关。