为了让使用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. Android API之android.widget.Filterable

      android.widget.Filterable 定义了一种可过滤的行为.Filterable接口通常有android.widget.Adapter来实现.接口Filterable中有个抽象方法 ...

  2. Spring Framework Ecosystem – Introduction to Spring Projects

    来自于:http://springtutorials.com/spring-ecosystem/ Hello and Welcome to Spring Tutorials Blog! Is it f ...

  3. PL/SQL配置oracle客户端,登录远程数据库配置

    本地未安装Oracle数据库,但又想使用PL/SQL连接服务器端的数据库. 1.新建NETWORK文件夹, 在该文件夹下新建ADMIN文件夹, 在该文件夹下新建tnsnames.ora文件(拷贝下面的 ...

  4. PLSQL连接ORACLE配置字符串简介 oracle网络配置 三个配置文件 listener.ora、sqlnet.ora、tnsnames.ora原理解释

    PLSQL连接ORACLE配置字符串简介 oracle网络配置 三个配置文件 listener.ora.sqlnet.ora.tnsnames.ora原理解释 oracle网络配置三个配置文件 lis ...

  5. Oracle自定义函数和存储过程示例,自定义函数与存储过程区别

    参考资料:http://www.newbooks.com.cn/info/60861.html oracle自定义函数学习和连接运算符(||) 贴一段中文文档示例,应该就可以开始工作了: --过程(P ...

  6. Tomcat JNDI + spring配置

    http://hi.baidu.com/lzpsky/item/f9a727ba823257eb4ec7fd27 一.简介 JNDI : Java Naming and Directory Inter ...

  7. android 登陆界面

    LoginActivity.java package com.example.ruian; import android.app.Activity; import android.app.AlertD ...

  8. PS_Form个性化选择Block自动查询和查询条件排序实现(案例)

    2014-06-01 BaoXinjian

  9. C语言sprintf与sscanf函数

    1.前言 我们经常涉及到数字与字符串之间的转换,例如将32位无符号整数的ip地址转换为点分十进制的ip地址字符串,或者反过来.从给定的字符串中提取相关内容,例如给定一个地址:http://www.bo ...

  10. LeetCode总结 -- 一维数据合并篇

    合并是一维数据结构中非经常见的操作, 一般是排序, 分布式算法中的子操作. 这篇总结主要介绍LeetCode中关于合并的几个题目: Merge Two Sorted ListsMerge Sorted ...