`
ASCII
  • 浏览: 31005 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

tomcat编码问题根源-1

阅读更多

    应用系统经常受到中文问题的困挠,J2EE环境下的中文问题更是常见。目前缺乏对此问题的全景分析,更有一些不合理的解决方案流传在网络。本文目的在于:

    分析中文问题的存在根源,解析完整的中文处理过程。
    中文问题涉及面很广,因为篇幅的所限,这里不会罗列各种现象的处理办法,而是就问题产生的原因进行探讨。
 
   1. 中文乱码问题的根源
    在J2EE系统的开发过程中,稀奇古怪的中文问题很多,一些运行好好的系统,修改了一个文件,或者迁移了一个平台,换了一个服务器,就变得面目全非了。为什么只有中文问题?没有英文问题?要解决中文问题,就应该从解答这个问题开始。

    1. 中文有多种编码。一个中文字符,在不同的环境下,有不同的二进制值
    2. 某些应用系统,会把字符串转换成其他的字符串进行传递或保存
    3. 在一个完整的软件中,各种编码形式存在很多的转换
    4. 每个转换过程都需要一些参数,比如“数据到底是Unicode还是GB2312的”
    5. 程序员往往会忘记设置这些参数,或者错误设置
    6. 不同的应用服务器或者api,其提供的设置方式经常不一样,甚至本身就是错误
    7. 另外,中文问题的泛滥还得益于一种有趣的现象:当程序中有两处出现编码错误的时候,错误可以相互抵消,产生看上去似乎正确的结果,从而成为随时可能爆发的定时** ,同时也使得程序员对错误做法产生错误的信心。
    8.最后,由于J2EE平台的开放性,各种工具和服务器来源广泛,而这些提供者对中文的处理办法往往不尽相同,有些厂家自己都不知道中文怎么回事(他们不是中国人)。JDK和Servlet本身也不得不因为双字节字符问题而修改。这给J2EE环境下的中文问题处理又增加了难度。

    2. 字符编码
    这个字符串变量的值是什么?ABC,还是“中文”?实际上,计算机里面无法保存“中文”这个方块字,同样也无法保存ABC这样的字符,只能保存二进制的数据。 
    因此计算机必须有办法用一个二进制的编码来代表字符,由此产生了ISO8859-1编码(和ASCII码相似),用一个二进制字节来代表英文字母及其他常见的字符。 
    2.1. 中文的编码
    遗憾的是,早期的计算机设计者并没有考虑到计算机将应用到全世界的各个领域,正如他们设计的日期只能到1999年为止一样。当计算机在我国应用的时候,问题就出现了。ISO8859-1并不包括中文,而且成千上万的汉字显然无法用一个字节来表示。
    其解决办法是双字节的GB2312码。因为要跟ISO8859-1码兼容,所以GB码一般以一种变长的EUC模式处理,即字符a的编码仍然和ISO8859-1一致,而汉字的编码,则用两个字节来表示。

    很麻烦吗?如果只有这么麻烦的话,恐怕就不会有那么多中文问题了。
首先,由于GB2312收录的字符有限,甚至连总理的名字都无法表示,因此又出现了GBK和GB18030编码(需要注意的是GBK并非国家标准)另外一些繁体字地区使用的BIG5编码,尽管原理类似,但是编码却不同,使得沟通非常困难。世界上其他地方,也有自己的双字节编码,字符编码成了“万马奔腾”的结局。如果大家各自用各自的,也就罢了,但是显然计算机专家不能容忍这种局面,于是,新的囊括几乎所以字符的双字节编码Unicode诞生了(其收录的字符仍然在增加中)。

    Unicode?那UTF-8又是什么东东呢?因为Unicode的编码,和ISO8859-1并不兼容,a的ISO8859-1编码为0x41,而 Unicode为 0x00 0x41。这样一来,那些长久以来积累的英文txt文件岂不是无法使用?而UTF-8是一种变长的编码,字母的编码和ISO8859-1完全一样,中文的编码,则用三个字节表示。

    通常在文本文件中使用的都是UTF-8编码,而jdk内部的String对象,才是Unicode编码。Java从设计之初,就明确了其所有的String对象,必然是Unicode编码。而字符串写到文件之中,或者供网络传输使用的时候,总之不是JVM的String对象的时候,则可能是UTF-8或者GB2312编码。

    所以汉字编码,有Unicode,GB2312和BIG5等形式。一个中文字符,在不同的软件不同的环境下,有不同的二进制值。虽然java String对象总是Unicode,但是J2EE是一个需要和很多应用系统打交道的环境,比如说,用户提交的数据,并不是java的String,而是浏览器提交的几个字节,配置文件的内容,是文件系统中的几个字节。

    然而即使如此,故事仍远未结束。
    2.2. 另一个故事:把任意数据编码成其他ISO8859-1字符串
  
    如果某协议只能使用可打印的ISO8859-1字符串(编码为0-127的字符,即字母,数字等),而现在需要处理图片数据,或中文数据,那怎么办?比如原本的邮件传输协议,就是如此。

    这就出现了BASE64编码,简单的说,它总是把3个字节的数据,转换成4个字节来表示,而这每个字节,都是一个“可打印”的ISO8859-1字符。 BASE64只使用64个字符,即字母,数字和加号,斜杠。根据使用的字符数量的不同,还有BASE85等编码。类似的情况,在别的应用领域还有其他编码。

    这些协议中最常见的,正是我们最“喜欢”的http。因为原本的http协议只能传递ISO8859-1字符串,所有的中文,不管是何种编码,都无法传递,其他任何双字节编码也一样。

    因此,http的url参数使用如下的形式:
    www.site.com/index.jsp?name=a&age=1&q=ÖÐÎÄ
ÖÐÎÄ “中”的GB2312码为十六进制的D6 D0,但是这两个字节根本无法放到http的协议中去,因此计算机用一个字符串来保存这些数据。把这两个字节用6个字符来表示:ÖÐ。接受方首先根据这个字符串还原字节数组D6 D0,加上知道使用的是GB2312码,就可以知道这6个字符代表的意思。

    又如,xml是纯粹的字符串文档,有很多符号都被语法本身所保留,如&
因此在xml文本中,一些字符需要用特殊的方式来表示,如&用&来代替。而更统一的方法,是用以下的形式
中文 表示“中文”
中文 同样表示“中文”,注意20013=x4e2d

    又如,JDK的native2ascii的运行结果(在java的properties文件中使用)
\u4e2d\u6587 表示“中文”

    让事情更糟的是,这些编码方式和前面的汉字编码并非相加的关系,而是相乘的关系,也就是说,可以用来编码GB2312的字符串,同样也可以用来编码Unicode的字符串。
    前面所列出的一些例子,分别是(文字都是“中文”):
    q=ÖÐÎÄ 
   “中文”的GB2312码 
    中文
   “中文”的Unicode码 0x4e 0x2d 0x65 0x87
    显然并没有谁规定这些方式只能编码Unicode或者UTF-8的数据,当然还可以是GB2312或者BIG5或者其他什么东西 
    2.3. 简单应用系统中的中文编码
    也许读者会问,这些跟我的中文问题有什么关系?我从来没有让我的程序使用过UTF-8或者那些古怪的字符串编码。
遗憾的是,每个程序员确实需要关心这些东西。不管你愿意不愿意,一个简单的java应用,已经在你并没有注意地情况下,使用了以上的很多编码。而这些编码过程在程序的处理过程中,就必须要经过很多次的转换,任何一个转换的错误,都可能是中文问题的根源!
     一个玩具性质的JSP应用,就已经使用了GB2312,Unicode,UTF-8,还有这些字符串所转换出来的各种形式。一个普通的J2EE应用,涉及的编码更是繁多。

    让我们来看一下在一个最简单的JSP页面中,“中文”两个字的各种表现形式。必须要说明一点的是,只有在应用程序完全按照tomcat的推荐方法设置的时候,以下的字符才会是这种形式,任何一个参数的不同,都将造成结果的不同。

    3. 字符转换过程
    我们已经看到,在一个过于简单的系统中,已经有3种以上的中文编码,繁多的字符串编码模式,组合出各种奇怪的数据。

    而所有这些在程序中运转的时候,都会涉及很多转换的过程。
    举简单的例子(以下的编码只是举例,并不是说在这个环节不能使用其他的编码):
    1. 用户的输入是GB2312,java需要读取成Unicode的字符串
    2. 如果在java代码中写了中文,java编译的类文件会用UTF-8来保存
    3. 配置文件使用了java的编码模式(\ u4e2d)
    4. 数据序列化XML的形式时,会把字符串转换成XML的字符编码(中) 
    读取的时候当然是相反的过程。 
    3.1. 一个真实的转换过程
    经常听到某人说“我这个String是GB2312的”,实际上,String对象永远是Unicode。JVM要处理这些数据,首先从外部的IO流读取字节数据,然后将其还原为String对象。

    举例来说,用户用http表单提交的数据中的“中”字,会用六个ISO8859-1字符表示:%D6%D0。注意这里的D6是两个字符,而不是十六进制的数字!他们的作用正是要代表D6 和 D0两个十六进制的数字。
当应用程序用request.getParameter来读取数据的时候,Tomcat处理字符转换大致是这样的原理(实际的处理过程当然决非这么简单,因为tomcat要考虑性能可靠性等因素)

import java.io.UnsupportedEncodingException; 

public class ConvertHttp2String 
 {   
        public static void main(String[] args) throws UnsupportedEncodingException   
        {   
               byte[] input = { 0x25, 0x44, 0x36, 0x25, 0x44, 0x30 };  
               System.out.println("http协议提交的数据为:" + new String(input, "ISO-8859-1"));   
               // 1 转换为16进制数,应该有两个16进制数 
               byte[] code = new byte[2];  
               for (int i = 0; i <= 1; i++)   
               {             
                      String temp = new String(input, i * 3, 3, "ISO-8859-1"); 
                      // 去掉百分号  
                      temp = temp.substring(1, 3); 
                      // 计算字符代表的16进制数   
                      code[i] = (byte) Integer.parseInt(temp, 16);  
                }   
                // 2 创建字符串,第一个参数为数据,第二个参数为编码类型  
                String result = new String(code, "GB2312");  
                System.out.println("代表字符串:" + result);     
         } 
} 

 

    输出结果:
    http协议提交的数据为:%D6%D0
    代表字符串:中

分享到:
评论

相关推荐

    彻底解决 Tomcat 5 下文字乱码问题 - JSP日志 - ※一路风尘※

    然而,在实际操作中,开发者时常会遇到字符编码问题,尤其是在Tomcat 5这样的较旧版本上。本文将深入探讨如何解决Tomcat 5中的文字乱码问题,尤其是针对JSP日志的处理。 首先,我们要理解乱码问题的根源。乱码通常...

    Tomcat乱码问题

    例如,当客户端发送的数据以UTF-8编码,而服务器却以ISO-8859-1编码读取时,就会出现乱码。 #### 二、JSP页面编码设置 为了解决JSP页面中的乱码问题,首先需要确保JSP页面的编码设置正确。在JSP页面头部添加以下...

    tomcat 异常

    1. **Tomcat异常类型**: - `NullPointerException`:这是Java中最常见的异常,表示尝试访问一个null对象的属性或调用其方法。 - `ClassCastException`:当试图将对象强制转换为它不是的类型时抛出。 - `...

    Hibernatetools编码格式的问题

    标题 "Hibernatetools编码格式的问题" 涉及的是在使用Hibernate Tools时遇到的编码相关问题。Hibernate Tools是Hibernate框架的一个扩展,它提供了一系列的辅助工具,如逆向工程(将数据库模式转化为Java实体类)、...

    JSP中文乱码问题完全处理方案.docx

    当用户通过POST方法提交包含中文的数据时,Tomcat默认使用ISO-8859-1编码处理,导致乱码。解决方法有两种:一是手动转码,如`new String(userName.getBytes("ISO-8859-1"), "gb2312")`,但这种方法需要在每个表单...

    Tomcat服务器图片地址中文路径问题解决办法

    这是因为Tomcat服务器默认使用ISO-8859-1编码来解码URI,而大部分现代浏览器在发送请求时,URL中的非ASCII字符会被按照UTF-8编码。 ISO-8859-1编码并不支持中文字符,因此当浏览器以UTF-8编码发送包含中文的URI时,...

    浅谈Tomcat乱码与端口占用的解决方案

    综上所述,通过调整字符编码解决乱码问题以及通过端口检测和管理解决端口占用问题,是确保Tomcat服务器正常运行的两个关键点。在日常的工作中,我们需要对这两个问题保持足够的警觉,并及时采取适当的措施进行应对。...

    java中文乱码终极处理方案.docx

    不过,实际上,很多问题的根源并不在于编程语言或框架本身,而是对编码和文件处理的误解。本文将深入探讨如何解决Java中的中文乱码问题。 首先,我们要理解编码的基础知识。常见的字符编码有UTF-8、GBK、GB2312和...

    Java开发乱码问题解决方法汇总

    乱码问题的根源之一是项目中的编码方式不统一。因此,在项目中统一使用一种编码方式是非常重要的。例如,在Eclipse中,可以右键点击项目,选择Properties,设置项目的文本文件编码方式为UTF-8。 2. 设置web.xml编码...

    Java编程技术中汉字问题的解决

    在Java中,默认的字符编码是Unicode(具体为UTF-16),而在网络传输中常用的编码有ISO-8859-1、GBK、GB2312等。当数据从一种编码转换到另一种编码时,如果没有正确的转换方式,就会导致乱码。 ### 解决方案:正确...

    web项目乱码问题 jsp乱码问题解决

    当表单数据以POST方式提交时,Tomcat默认使用iso8859-1编码处理请求参数,而接收端JSP可能期望的是UTF-8编码,这就需要采取措施进行转换。 解决POST提交乱码的方法有以下几种: A. 手动转换:在接收参数时,先将...

    Jsp和Servlet中文乱码问题

    这里,先使用`ISO-8859-1`编码读取参数,然后将其转换为`GB2312`编码。然而,更推荐的做法是直接在Tomcat的配置中设置统一的编码,从而简化编码处理逻辑。 ### 预防策略 为了从根本上解决中文乱码问题,建议采取...

    jsp中文乱码问题小结

    对于输入解码,JSP在处理POST请求时,若服务器默认编码为ISO-8859-1,中文会出现乱码。此时,可以在JSP页首设置`;charset=GBK"%&gt;`,或在Servlet中调用`request.setCharacterEncoding("GBK")`。对于URL编码,可以使用...

    java乱码自己解决的办法

    一种常见的做法是在接收到参数后,先将其按照服务器默认的编码(通常是ISO-8859-1)转成字节数组,然后再按照目标编码(如UTF-8)进行解码: ```java String keyword = new String(request.getParameter("keyword")...

    javaweb servlet(jsp)的乱码问题原理及解决

    ### javaweb servlet(jsp)的乱码问题原理及...需要注意的是,编码问题的根源在于不同组件间编码设置的不一致,因此在设计和实现时需充分考虑编码兼容性和统一性,确保数据在整个传输过程中都能正确地被解码和呈现。

    系统中文乱码解决方案

    - **目的**:设置Tomcat编码以防止GET请求时出现乱码。 - **配置示例**: ```xml URIEncoding="UTF-8" connectionTimeout="20000" maxThreads="150" port="8888" protocol="HTTP/1.1" redirectPort="8443"/...

    JSP中文乱码问题分析及处理方法

    尤其是在客户端读取服务器端输出以及服务器端读取客户端输入时,若未指定正确的编码格式,系统将默认使用ISO8859-1编码,这对于中文字符来说是不适用的,从而导致乱码。 #### 二、解决方案详解 针对JSP中文乱码...

    java web 开发乱码汇总

    当使用Ajax发送请求时,JavaScript默认使用ISO-8859-1编码,因此需要对包含中文的参数进行编码。一种常见做法是在发送请求前使用`encodeURI()`或`encodeURIComponent()`方法对参数进行编码,例如`var url = ...

    乱码问题深度分析课题划分

    本文将深入探讨乱码问题的根源、常见场景以及解决策略,旨在为IT从业者提供一个全面的理解框架。 #### 1. 字符集与乱码现象 乱码,通常是指计算机系统或软件在处理文本时,由于字符编码不一致或转换错误,导致显示...

Global site tag (gtag.js) - Google Analytics