中文乱码原因解析

来源:互联网 发布:龙卷风网络收音机 mac 编辑:程序博客网 时间:2024/06/06 05:52

编码与解码的基本概念:

1、 系统:指可以按指定的编码方式 源输入流(即数据源)中读取字符数据,并以该系统的编码方式将读取到的字符数据存储在内存中,在需要输出时,可以按指定的编码方式将字符数据编码并写入到目的输出流中。例如:javaJVM,它能利用一些输入流按指定的编码方式来读取数据源中存储的编码形式的二进制数据,并将读取到的数据按Unicode编码方式存储在内存中,等到输出时,又将Unicode形式的数据转换 为指定编码方式的数据写入到输出流中。Tomcat带有JSP编译器,它会根据JSP文件开头的<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>中的pageEncoding识别读取JSP文件并编译,然后根据contentType里面的Charset将响应数据写成对应编码方式的二进制数据发送给客户端,并在http响应头中告诉浏览器响应数据的编码方式。浏览器根据HTTP响应头中指定的字符编码来解析HTML内容

2、 数据源:顾名思义是数据的来源,即存放数据的源头,数据最终的宿主(即寄存的地方)终究是文件。在计算机的范畴内,这个数据是指二进制数据,即10的序列,类似于11111100010。数据源又分文本文件(text file)二进制文件(binary file).

3、 文本文件:存储的二进制数据是指定的编码方式(比如UTF-8,GBK,ISO-8859-1,ASCII)的二进制编码数据,在计算机上按该指定的编码方式打开是可以看到该编码方式下对应的字符,如果不以对应的指定编码方式来打开,则会乱码,比如存储的是GBK编码方式的数据,但是却用UTF-8识别来显示,就会出现乱码,因为GBK是用2个字节来表示一个中文字符,而UTF-8用三个字节来表示一个中文字符。JVM读取文本文件会有个转码的过程,即将该文本中的某种编码方式的二进制数据转换为Unicode编码形式的二进制数据存储到内存中。

4、 二进制文件:存储的二进制数据是内存中的直接数据(即Unicode编码方式的二进制数据),在计算机上打开它看到的是乱码,用UTF-16Unicode的对应的编码方式)编码方式来打开才会显示正常的字符。JVM读取二进制文件不需要转码的过程,因为读取到的二进制数据已经是Unicode编码方式的二进制数据。

5、 编译:  eclipse内置编译器会根据源文件编码方式来读取源文件,并编译成Unicode编码方式的字节码文件(即.class文件,它存储的是Unicode格式的二进制数据)。一些独立的编译器可以在设置指定的编码方式来读取源文件并编译成最终的.class字节码文件。

6、 转码:就是将一种编码方式的二进制数据通过中间通用的编码方式数据(java中是Unicode)转换成另一种编码方式的二进制数据,但它们表示的是相同的字符。“测试”这两个中文字符,他的UTF-8编码为%E6%B5%8B%E8%AF%95,而GBK编码为%B2%E2%CA%D4(其中的B2E2CAD4都是16进制数据,它对应的Unicode二进制数据为1011 0010 1110 0010 1100 1010 1101 0100)。

解决中文乱码问题:

1、服务器响应客户端时出现乱码

response.setCharacterEncoding("utf-8") 该句使程序向客户端发送uft-8格式的响应数据

response.setContentType("text/html;charset=utf-8"); 该句在http响应头里面设置了mime类型,用于告诉浏览器用utf-8来识别响应的数据。如果这两处的编码不一致,就会导致浏览器显示乱码

 

2、客户端向服务器提交数据时出现乱码

1)针对post方法

request.setCharacterEncoding("utf-8");

servlet里面写入该句,告诉程序使用“utf-8”识别传送过来的二进制数据,如果传过来的二进制数据是utf-8格式,则不会乱码,否则就乱码。改句只对post请求有效,对get请求无效,get请求默认使用ISO-8859-1识别。

2)针对get方法:

对于Get请求,在没修改%TOMCAT_HOME%/conf/server.xmlConnector给它加上URIEncoding="编码方式"时,程序一致使用默认的ISO-8859-1识别传过来的二进制数据,加上时就使用指定的编码方式识别。

修改%TOMCAT_HOME%/conf/server.xml

<Connector port="8080" protocol="HTTP/1.1"

               connectionTimeout="20000"

               redirectPort="8443" URIEncoding="utf-8"/>

 

编码与解码示例

String name;

name = java.net.URLEncoder.encode("测试","UTF-8");

System.out.println("编码:"+name);

输出的是 %E6%B5%8B%E8%AF%95 ,即“测试”中文字符的对应的UTF-8编码。程序运行时,是运行编译后的.class字节码文件,所以

java.net.URLEncoder.encode函数读取到的“测试”中文字符是Unicode编码格式的二进制数据。它的返回值为“%E6%B5%8B%E8%AF%95”字符串,这个字符串也是Unicode编码格式表示的,但它表示了“测试”中文字符的UTF-8编码是E6 B5 8B E8 AF 95

 

System.out.println(java.net.URLDecoder.decode("%E6%B5%8B%E8%AF%95", "UTF-8"));

注意到"%E6%B5%8B%E8%AF%95"“测试”中文字符的UTF-8编码字符串,java.net.URLDecoder.decode函数在读取到的%E6%B5%8B%E8%AF%95字符串也是Unicode二进制编码数据,但是该函数内部将根据因该Unicode二进制编码数据中包含‘%’字符的Unicode二进制编码数据判定该字符串是某种编码方式字符串,将舍弃%’字符的Unicode二进制数据得到二进制编码数据E6 B5 8B E8 AF 95(在utf-8中对应于“测试”中文字符),然后再用utf-8编码方式识别并转换为“测试”对应的Unicode二进制编码数据,它就是该函数的返回值。

 

System.out.println(java.net.URLDecoder.decode("测试", "任意字符"));

只要“任意字符”它不空,输出的都是“测试”

 

 

 

 

 

另外,在Web应用的url中传中文参数时,

经测试,在IE浏览器的地址栏直接打带中文参数的url,它发送的是GBK格式的二进制数据,而ChromeFirefoxUTF-8。这是因为通常浏览器会用两种编码方式发送URL到服务器,分别是UTF-8ANSI

而中文Windows环境下,IE中默认发送的URLPATH部分UTF-8编码参数部分GBK编码IE的设置选项中有 一项是“总是以 UTF-8 发送URL”可以改变发送URL的编码为ANSI

中文Windows环境下FireFox中默认发送的URLPATH和参数都是UTF-8编码,在FireFox地址栏输入 “about:config”,找到选项“network.standard-url.encode-utf8”,即可改变发送URL的编码方式。

Chrome浏览器默认url的默认编码格式是UTF-8

建议不要让用户在地址栏打入中文进行操作;

当编码格式是UTF-8时,我们输入url如下

http://localhost:8080/lpfWeb/testServlet.do?test=%E6%B5%8B%E8%AF%95

这个url等同于http://localhost:8080/lpfWeb/testServlet.do?test=测试

它们发test参数都是utf8格式的二进制数据E6 B5 8B E8 AF 95

url

http://localhost:8080/lpfWeb/testServlet.do?test=%25E6%25B5%258B%25E8%25AF%2595   %25E6%25B5%258B%25E8%25AF%2595是%E6%B5%8B%E8%AF%95字符串的编码字符串,即使“测试”的编码的编码字符串。它发送的test参数是utf8格式的二进制数据25E6 25 B5 25 8B 25 E8 25 AF 25 95。其实25就是‘%’的Unicode编码,值为37

1 0