为了让使用Java语言编写的程序能在各种语言的平台下运行,Java在其内部使用Unicode字符集来表示字符,这样就存在Unicode字符集和本地字符集进行转换的过程。当在Java中读取字符数据的时候,需要将本地字符集编码的数据转换为Unicode编码,而在输出字符数据的时候,则需要将Unicode编码转换为本地字符集编码。
例如,在中文系统下,从控制台读取一个字符“中”,实际上读取的是“中”的GBK编码0xD6D0,在Java语言中要将GBK编码转换为Unicode编码0x4E2D,此时,在内存中,字符“中”对应的数值就是0x4E2D,当我们向控制台输出字符时,Java语言将Unicode编码再转换为GBK编码,输出到控制台,中文系统再根据GBK字符集画出相应的字符。
从上述过程来看,读取和写入的过程是可逆的,那么理应不会出现中文乱码问题。然而,实际应用的情形,比上述过程要复杂得多。在Web应用中,通常都包括了浏览器、Web服务器、Web应用程序和数据库等部分,每一部分都有可能使用不同的字符集,从而导致字符数据在各种不同的字符集之间转换时,出现乱码的问题。
在Java语言中,不同字符集编码的转换,都是通过Unicode编码作为中介来完成的。例如,GBK编码的字符“中”要转换为ISO-8859-1(同ISO8859-1)编码,其过程如下:
(1)因为在Java中的字符,都是用Unicode来表示的,所以GBK编码的字符“中”要转换为Unicode表示:0xD6D0->0x4E2D。
(2)将字符“中”的Unicode编码转换为ISO-8859-1编码,因为Unicode编码0x4E2D在ISO-8859-1中没有对应的编码,于是得到0x3f,也就是字符“?”。
下面的代码演示了这一过程:
//GBK编码的字符“中”转换为Unicode编码表示
String str="中";
//将字符“中”的Unicode编码转换为ISO-8859-1编码
byte[] b=str.getBytes("ISO-8859-1");
 
for(int i=0;i<b.length;i++)
{
    //输出转换后的二进制代码。
    System.out.print(b[i]);
}
当从Unicode编码向某个字符集转换时,如果在该字符集中没有对应的编码,则得到0x3f(即问号字符?)。这就是为什么有时候我们输入的是中文,在输出时却变成了问号。
从其他字符集向Unicode编码转换时,如果这个二进制数在该字符集中没有标识任何的字符,则得到的结果是0xfffd。例如一个GBK的编码值0x8140,从GB2312向Unicode转换,然而由于0x8140不在GB2312字符集的编码范围(0xa1a1-0xfefe),当然也就没有对应任何的字符,所以转换后会得到0xfffd。下面的代码演示了这一过程。
//构造一个二进制数据。
byte[] buf={(byte)0x81,(byte)0x40,(byte)0xb0,(byte)0xa1};
//将二进制数据按照GB2312向Unicode编码转换。
String str=new String(buf,"GB2312");
       
for(int i=0;i<str.length();i++)
{
    //取出字符串中的每个Unicode编码的字符。
    char ch=str.charAt(i);
    //将该字符对应的Unicode编码以十六进制的形式输出。
    System.out.print(Integer.toHexString((int)ch));
    System.out.print("--");
    //输出该字符。
    System.out.println(ch);
}
在输出字符和字符串的时候,会从Unicode编码向中文系统默认的编码GBK转换,由于Unicode编码0xfffd在GBK字符集中没有对应的编码,于是得到0x3f,输出字符“?”。最后输出的结果如下:
fffd--?
40--@
554a--啊
从上述所知,由于存在着多种不同的字符集,在各种字符集之间进行转换,就有可能出现乱码,同样是中文字符集GB2312和GBK,由于编码范围的不同,某些字符在转换时也会出现乱码。
在一个使用了数据库的Web应用程序中,乱码可能会在多个环节产生。由于浏览器会根据本地系统默认的字符集来提交数据,而Web容器默认采用的是ISO-8859-1的编码方式解析POST数据,在浏览器提交中文数据后,Web容器会按照ISO-8859-1字符集来解码数据,在这一环节可能会导致乱码的产生。由于大多数数据库的JDBC驱动程序默认采用ISO-8859-1的编码方式在Java程序和数据库之间传递数据,我们的程序在向数据库中存储包含中文的数据时,JDBC驱动首先将程序内部的Unicode编码格式的数据转化为ISO-8859-1的格式,然后传递到数据库中,在这一环节可能会导致乱码的产生。目前流行的关系型数据库系统都支持数据库编码,也就是说在创建数据库时可以指定它自己的字符集设置,数据库的数据以指定的编码形式存储。当JDBC驱动向数据库中保存数据时,有可能还会发生字符集的转换。正是由于在Web应用程序运行过程中,输入的中文字符需要在不同的字符集之间来回转换,也就导致了中文乱码问题的频繁出现。
图17-1描述了在Web应用的请求响应过程中,发生的字符编码转换过程,其中浏览器是IE 6.0,Web容器的是Tomcat 6.0.16。
从图17-1描述的过程中可以看到,如果在Web应用程序中不指定任何的字符集,从浏览器端传来的中文字符,输出回浏览器时,可以正常显示(以简体中文的方式查看网页)。然而,事情并没有这么简单,在Servlet/JSP中,可能存在着直接写入的或从其他来源读取的中文字符,如果这些字符对应的Unicode码是从GB2312编码转换而来,那么以ISO-8859-1编码方式输出,这些字符将不能正常显示。所以对于中文的处理,应该在图17-1②和⑤的位置明确指定使用GB2312或GBK字符集。
 
图17-1  在Web请求响应过程中,中文字符编码的转换过程

web应用中文乱码问题的原因分析的更多相关文章

  1. JavaWeb使用Filter进行字符编码过滤 预防web服务中文乱码

    JavaWeb使用Filter进行字符编码过滤 预防web服务中文乱码 准备条件:一个创建好的 JavaWeb 项目 步骤: 1.创建一个类并实现 Filter 接口 import javax.ser ...

  2. TimesTen数据库表中显示中文乱码的真正原因

    上一篇博客TimesTen中文乱码问题(其实是cmd.exe中文乱码)的内容可能不对,也许只是个巧合?不得而知了.因为我今天重装系统了,把win10换成了win7(64bit).又安装了timeste ...

  3. 1.JAVA WEB 笔记中文乱码

    JAVA WEB 乱码问题解析 乱码原因 在Java Web开发过程中,经常遇到乱码的问题,造成乱码的原因,概括起来就是对字符编码和解码的方式不匹配. 既然乱码的原因是字符编码与解码的方式不匹配,那么 ...

  4. myql数据库在cmd下,中文乱码的问题原因

    使用navicat把数据导入数据库,这些数据都是中文,导入成功,显式也正常,但是在mysql cmd下都是乱码.检查了我的mysql配置,字符编码都是utf8,包括navicat连接时候也设置过是ut ...

  5. 如何解决WEB应用中文乱码问题

    最容易出现乱码的场景是浏览器向服务器发送请求的过程,解决的策略就是强制为请求和响应设置编码格式. 一.POST 方式请求乱码 1. 在每个 Controller 中单独处理 request.setCh ...

  6. 第三章 深入分析Java Web的中文乱码问题(待续)

    几种常见的编码格式 在Java中需要编码的场景 在Java中如何编解码 在Java Web中涉及的编解码 在JS中的编码问题 常见问题分析 一种繁简转换的实现方式

  7. 解决Ubuntu下在firefox中打开Microsoft Outlook Web Access中文乱码

    Edit---Preference--Content--Languages--Choose...---Select a langue to add... 添加中文

  8. Web | 解决中文乱码

    设定文件的编码格式在head中添加 <head> <meta http-equiv="Content-Type" content="text/html; ...

  9. JMeter学习笔记(十八)——返回的响应数据出现中文乱码_解决方案

    一.问题描述 使用jmeter过程中遇到了请求返回的响应数据出现中文乱码 二.原因分析 当没有对响应数据or响应页面设置支持解析中文的编码时,JMeter则会以默认的ISO-8859-1格式解析,而其 ...

随机推荐

  1. 通过配置Apache实现404页面替换

    一.通用情况--修改apache配置.htaccess 一般网站报404原因都是找不到资源,是服务器(以Apache为例)报错,Apache自定义了404输出,我们的目的是使用自定义的404.html ...

  2. MVC笔记 Controller相关技术

    一.Controller的责任 MVC的核心就是Controller(控制器),它负责处理浏览器传送过来的所有请求,并决定要将什么内容响应给浏览器.但Controller并不负责决定内容应该如何显示, ...

  3. RabbitMQ消息队列(三):任务分发机制[转]

    在上篇文章中,我们解决了从发送端(Producer)向接收端(Consumer)发送“Hello World”的问题.在实际的应用场景中,这是远远不够的.从本篇文章开始,我们将结合更加实际的应用场景来 ...

  4. xcode 在哪里新建category、protocol等文件

    1.和以前新建新文件一样.2.当然选IOS啦,不过OS X也有这个选项,然后Objctive-C File. 3.在File Type里选就OK啦.

  5. epoll 系列函数简介、与select、poll 的区别

    一.epoll 系列函数简介 #include <sys/epoll.h> int epoll_create(int size); int epoll_create1(int flags) ...

  6. javascript常用字符串函数和本地存储

    concat将两个或多个字符的文本组合起来,返回一个新的字符串.var a = "hello";var b = ",world";var c = a.conca ...

  7. Android开发1——查找所需要出示权限的内容

    一.发现问题 用户在执行一些如拨打电话.发送短信等关系用户隐私的功能时,Android需要出示权限,权限在AndroidManifest.xml中配置 拨打电话的权限 发送短信的权限 那么这些权限信息 ...

  8. angular.js 中的作用域 数据模型 控制器

    1.angular.js 作为后起之秀的前端mvc框架,他于传统的前端框架都不同,我们再也不需要在html中嵌入脚本来操作对象了.它抽象出了数据模型,控制器及视图. 成功解耦了应用逻辑,数据模型,视图 ...

  9. ASP.NET Web API 中 特性路由(Attribute Routing) 的重名问题

    刚才忘了说了,在控制器名重名的情况下,特性路由是不生效的.不然的话就可以利用特性路由解决同名的问题了. 而且这种不生效是真的不生效,不会提示任何错误,重名或者什么的,直接会报告404,所以也是个坑.

  10. 高度自适应的CSS

    /*高度自适应*/ .com_fill_height{ height:100%; overflow:hidden; } 高度自适应的样式代码,真的就这么简单吗...