- 浏览: 13623 次
- 性别:
- 来自: 北京
最新评论
-
bjrobin:
这个没有视频。对不起
尚学堂.张志宇.TOMCAT集成APACHE.doc -
傲雪木瓜露:
求配套的视频,有么
尚学堂.张志宇.TOMCAT集成APACHE.doc -
因为青所以涩:
楼主厉害,从本质上分析了乱码产生的原因
尚学堂.张志宇.乱码分析_02_servlet乱码.doc
1. 结论
l pageEncoding 会影响读取 jsp 时的解码过程。
l pageEncoding 会影响转换后的 servlet 的 response.setContentType 这句话。
l 对于不合法的 utf-8 编码进行解码,每个字节都会得到 ff fd 。这个代码点代表在 unicode 中不存在或者不可表示的字符
|
场景 1 |
场景 2 |
场景 3 |
|
1.jsp 存储格式 |
gbk |
gbk |
存为 gbk |
|
<%@page pageEncoding |
gbk |
utf-8 |
pageEncoding="utf-8" |
|
浏览器字符编码 |
gbk |
utf-8( 默认 ) |
gbk (手工设定) |
|
转换后的 servlet |
我们 |
����! |
����! |
|
浏览器 |
我们 |
����! |
锟斤拷锟斤拷 ! |
|
2. 场景 1
没有乱码,分析略过
3. 场景 2
1.jsp
<%@page pageEncoding=" utf-8"%> <%=(" 我们 !")%> |
详细分析
l 首先, 1.jsp 文件默认存储为 ansi ,即 jbk , ” 我们 !” 编码成 jbk 得到如下字节 ce d2 c3 c7 21
l 在 tomcat 将 jsp 转换为 servlet 的过程中,需要先读入 jsp 中的字符信息,因为 jsp 是按照 gbk 来存储的,如果 tomcat 按照 gbk 来解码,能够得到正确的中文 ;但是由于在 jsp 文件中指明 pageEncoding 为 utf-8 ,所以 tomcat 会把硬盘上的 jbk 编码错误的当作 utf-8 来解码。 将解码之后得到的错误的字符串再编码成 utf-8 存储在转换后的 servlet 源文件 _1_jsp.java 中 .
n 用 ultraedit 打开 tomcat 工作目录 work\Catalina\localhost\my\org\apache\jsp 目录下的 _1_jsp.java ,切换到 16 进制可以看到这些字节, ef bf bd ef bf bd ef bf bd ef bf bd 21 , 这些字节就是错误的 utf-8 编码。
n 为什么会编码成这些字节?
先来回忆一下 uft-8 的编码规则。 UTF-8 用 1 ~ 4 个字节来表示代码点。表示方式如下:
将 ce d2 c3 c7 解码的意思是说,这些字节是合法的 utf-8 编码,现在要还原成正确的字符。 问题是: ce d2 c3 c7 是合法的 utf-8 编码吗?
ce d2 c3 c7 对应的 2 进制是: 11001110 11010010 11000011 11000111
先看 ce 对应的二进制是 11001110 , 110 开头说明是符合 uft-8 的编码规则第二行,范围是 U+0080 .. U+07FF 的 unicode 会被编码成 110xxxxx 10yyyyyy , 紧跟着下来的第二个字节应该是 10 开头,但是,接下来的 d2 却是 110 开头的。说明这不是个合法的 utf-8 编码。
ce d2 c3 c7 不是合法的 utf-8 编码! 或者可以这样说,这些所谓的 utf8 编码对应的字符在 unicode 中就根本不存在。!
在接着往下看! 虽然错误解码了。但是字符串对象还是得到了。只不过不是我们想要的而已。 在错误解码后得到的字符串应该在内存中表示为 utf-16 编码吧。 既然这些不合法的 utf-8 编码 对应的字符在 unicode 中就根本不存在。那内存里怎么办?
先来看 ef bf bd 的二进制是 11101111 10111111 10111101 根据 utf-8 编码规则,它代表的是 unicode 中的 ff fd 代码点。
打开 UFFF0.pdf 来验证下。(这个文档可以去 unicode 官网去下载) 看看 unicode 关于 fffd 这个代码点的解释: used to replace an incoming character whose value is unknown or un representable in Unicode. 哦,用来代表在 unicode 中未知的或者无法表示的字符
来稍微总结一下: 我们要对 ce d2 c3 c7 按照 utf-8 进行解码,当然这些字节不是合法的 utf-8 编码。 解码的目的是得到一些个字符。我们得到了所谓的字符。一共 4 个,这 4 个字符在 unicode 里面的代码点都是 ff fd 。在 unicode 中 ff fd 这个代码点代表未知的或者无法表示的字符。 对于这个代码点,当然有合法的 utf-8 编码,即 ef bf bd
|
l 打开 _1_jsp.class 这个文件,同样可以发现 ef bf bd ef bf bd ef bf bd ef bf bd 21 这些字节,说明 class 文件也错误的 utf-8 编码!
l servlet 开始运行,会将字节码里面的错误的 utf-8 编码按照 utf-8 进行解码。得到的当然是错误的内容。错误解码得到的错误的字符串在内存里标识为 utf-16 编码。这时候内存里面应该是 ff fd ff fd ff fd ff fd 0 21
l 由于 jsp 中咱们是这么写的 <%@page pageEncoding=" utf-8"%> ,这样会造成转换 servlet 时自动产生这句 response.setContentType("text/html;charset=utf-8"); 。这句代码的意思是输出到浏览器之前,按照 utf-8 进行编码。这将得到 ef bf bd ef bf bd ef bf bd ef bf bd 21 ,这些直接被传到浏览器。
n 可以靠下面的代码来验证,客户端浏览器是不是得到这些字节呢?
import java.io.BufferedReader; import java.io.BufferedWriter ; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket;
public class TestHTTP2 { public static void main(String[] args) throws Exception { Socket s = new Socket( "127.0.0.1" , 80); BufferedWriter bw = new BufferedWriter ( new OutputStreamWriter(s .getOutputStream())); bw.write( "GET /my/1.jsp HTTP/1.1" ); bw.newLine(); bw.write( "Host: 127.0.0.1" ); bw.newLine(); bw.write( "Content-Type: text/html" ); bw.newLine(); bw.newLine(); bw.flush();
BufferedReader br = new BufferedReader( new InputStreamReader(s .getInputStream())); String str = null ; while ((str = br.readLine()) != null ) { System. out .println(str); } bw.close(); br.close(); s.close(); } }
|
这段程序得到下面的结果。“锟斤拷锟斤拷 ! ” 这个怪怪的字符串是按照 gbk 解码得到的结果。
这个结果没法直观的了解客户接收的字节信息。
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Set-Cookie: JSESSIONID=7D6AE9D95A8D2562AD6B19A4DE0EE08C; Path=/my Content-Type: text/html;charset=utf-8 Content-Length: 15 Date: Tue, 25 Aug 2009 03:29:09 GMT
锟斤拷锟斤拷 !
|
程序稍微改动一下,原来用的是转换流。现在只用基于字节的输入流,不做任何转换。
import java.io.BufferedWriter; import java.io.InputStream; import java.io.OutputStreamWriter; import java.net.Socket;
public class TestHTTP1 { public static void main(String[] args) throws Exception { Socket s = new Socket( "127.0.0.1" , 80); BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(s .getOutputStream())); bw.write( "GET /my/1.jsp HTTP/1.1" ); bw.newLine(); bw.write( "Host: 127.0.0.1" ); bw.newLine(); bw.write( "Content-Type: text/html" ); bw.newLine(); bw.newLine(); bw.flush();
InputStream ins = s.getInputStream(); int i = 0; while ((i = ins.read ()) != -1) { System. out .format( "%x " , i); } ins.close(); bw.close(); ins.close(); s.close(); } }
|
48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b d a 53 65 72 76 65 72 3a 20 41 70 61 63 68 65 2d 43 6f 79 6f 74 65 2f 31 2e 31 d a 53 65 74 2d 43 6f 6f 6b 69 65 3a 20 4a 53 45 53 53 49 4f 4e 49 44 3d 33 43 36 32 36 42 33 35 43 43 43 38 42 32 31 37 38 37 36 34 38 30 37 36 42 38 42 42 38 46 41 44 3b 20 50 61 74 68 3d 2f 6d 79 d a 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 74 65 78 74 2f 68 74 6d 6c 3b 63 68 61 72 73 65 74 3d 75 74 66 2d 38 d a 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 31 35 d a 44 61 74 65 3a 20 54 75 65 2c 20 32 35 20 41 75 67 20 32 30 30 39 20 30 33 3a 32 37 3a 34 30 20 47 4d 54 d a d a d a ef bf bd ef bf bd ef bf bd ef bf bd 21 |
ef bf bd ef bf bd ef bf bd ef bf bd 21 ,这些内容就是浏览器收到的内容。
l 由于浏览器收到的头信息是: Content-Type: text/html;charset=utf-8 ,浏览器就会默认 按照 utf-8 进行解码。看看浏览器的字符编码是不是默认选在了 utf-8 ?
解码之后得到的是不存在的字符,所以
在 eclipse 中显示为;得到的是 ????!
在浏览器看到的是 ����!
4. 场景 3
l 如果客户端浏览器把这些字节 ef bf bd ef bf bd ef bf bd ef bf bd 21 按照 gbk 来解码,得到的是
锟斤拷锟斤拷 !
5. 验证代码
import java.io.UnsupportedEncodingException;
public class TestC { public static void main(String[] args) throws UnsupportedEncodingException { //jsp 文件默认存储为 ansi ,即 jbk 编码: ce d2 c3 c7 21 byte [] bytes = new byte []{( byte )0xCE,( byte )0xD2,( byte )0xc3 ,( byte )0xc7 ,( byte )0x21 };
// 如果按照 gbk 来解码,能够得到正确的中文 System. out .println( "-------- 如果按照 gbk 来解码,能够得到正确的中文 " ); String s = new String(bytes, "gbk" ); System. out .println(s);
// 但是由于在 jsp 文件中指明 pageEncoding 为 utf-8 , // 所以 tomcat 会把硬盘上 1.jsp 的 jbk 编码错误的当作 utf-8 来解码。 String s1 = new String(bytes, "utf-8" ); System. out .println( "--------tomcat 会把硬盘上 1.jsp 的 jbk 编码错误的当作 utf-8 来解码 " ); System. out .println(s1);
System. out .println( "--------servlet 源文件 h 和字节码文件都是这些 utf-8 编码 " ); // 将解码之后得到的错误的字符串再编码成 utf-8 存在转换的 servlet 源文件中。 //servlet 的 class 文件也是这些 utf-8 编码 byte [] bytes1 = s1.getBytes( "utf-8" ); for ( byte b:bytes1){ System. out .format( "%x " ,b); } System. out .println();
//servlet 载入执行,将字节码中的 utf-8 编码解码成字符串对象。 // 如果再按照 utf-16 编码,我们可以看到内存里面的 utf-16 编码。 System. out .println( "-------- 内存里面的 utf-16 编码 " ); String s2 = new String(bytes1, "utf-8" ); byte [] bytes_uft_16 = s2.getBytes( "utf-16" ); for ( byte b:bytes_uft_16){ System. out .format( "%x " ,b); } System. out .println();
// 被编码成 utf-8 ,传到浏览器客户端 System. out .println( "-------- 被编码成 utf-8 ,传到浏览器客户端 " ); byte [] bytes2 = s1.getBytes( "utf-8" ); for ( byte b:bytes2){ System. out .format( "%x " ,b); }
// 客户端按照 utf-16 解码 String s3 = new String(bytes1, "utf-8" ); System. out .println(); System. out .println( "-------- 客户端按照 utf-16 解码 " ); System. out .println(s3); } }
|
- 尚学堂.张志宇.乱码分析_08_pageEncoding.rar (14 KB)
- 下载次数: 22
发表评论
-
尚学堂.张志宇.TOMCAT集成APACHE.doc
2012-02-23 12:30 1196TOMCAT 集成 APACHE 1 ... -
从源码安装tomcat 6.0.18
2012-02-23 12:27 964从源码安装 tomcat 6.0.18 注意 ... -
尚学堂.张志宇.乱码分析_07_form表单存储格式.doc
2012-01-31 10:31 10251 结论 l ... -
我将不定期的发表一些我写过的文章上来,希望对大家有帮助
2012-01-30 23:42 693:):):) -
尚学堂.张志宇.乱码分析_04_读取硬盘上的文件.doc
2012-01-30 23:39 957为什么是?í?ó 1 结论 J2SE 5.0 用的是Uni ... -
尚学堂.张志宇.乱码分析_03_读取servlet参数.doc
2012-01-30 23:35 10791 重要结论 J2SE 5.0 用的是Unicode 4.0 ... -
尚学堂.张志宇.乱码分析_02_servlet乱码.doc
2012-01-29 14:15 12031 重要结论 J2SE 5.0 用的是Unicode 4.0 ... -
尚学堂.张志宇.乱码分析_01_基础.doc
2012-01-29 14:11 11771. 什么是ASCII码 ASCII(Am ...
相关推荐
hadoop大数据培训零基础学习hadoop-北京尚学堂.docxhadoop大数据培训零基础学习hadoop-北京尚学堂.docx
【标题】:尚学堂_Spring_0200_IOC_Introduction 【内容详解】: Spring 框架是 Java 应用开发中的一个重要组件,它以其依赖注入(Dependency Injection,简称 DI)和控制反转(Inversion of Control,简称 IOC)...
总之,"Spring_0700_IOC_Collections"涵盖了Spring框架中关于IoC容器处理集合依赖的重要知识,包括XML配置、Java配置、注解注入以及源码分析等多个方面。通过学习这部分内容,开发者可以更深入地理解Spring的IoC机制...
标题中的"Spring_0300_IOC_Injection_Type"指的是Spring框架中的依赖注入(Dependency Injection,简称DI)机制,特别是关于类型注入(Type Injection)的知识点。在Spring框架中,依赖注入是核心特性之一,它使得...
JavaSE(Java Standard Edition)是Java编程语言的基础部分,涵盖了从基本语法到核心特性的一系列知识。本资料集“百战程序员答案javase基础部分”是针对Java初学者和进阶者的宝贵资源,包含了从第一章到第十章的...
标题中的"Spring_0800_IOC_Auto_Wire"指的是Spring框架中的依赖注入(Dependency Injection,简称DI)特性,特别是自动装配(Auto Wiring)。在Spring框架中,IOC(Inversion of Control,控制反转)是一种设计模式...
标题中的“Spring_0600_IOC_Bean_Scope”涉及到的是Spring框架中的核心概念——控制反转(Inversion of Control, 简称IOC)以及Bean的作用域(Scope)。在这个主题下,我们将深入探讨Spring如何通过IOC管理Bean的...
最后,建议配合尚学堂的肖斌Hadoop经典视频教程以及云计算极限班的课程进行系统学习,这些资源将提供详细的指导和实例,帮助你逐步深入理解和掌握Hadoop大数据技术。同时,持续实践和参与社区讨论也是提升技能的重要...
1. 尚学堂_设计模式_责任链_01.avi:这个视频可能从基础开始介绍责任链模式,阐述其核心思想,比如如何定义处理器对象,以及如何建立和配置责任链。 2. 尚学堂_设计模式_责任链_02.avi:接着,可能会讲解如何在实际...
《尚学堂_Spring_0100_模拟Spring》 在深入探讨Spring框架之前,我们需要先理解Spring的核心理念。Spring是一个开源的Java平台,它主要为构建企业级应用提供了全面的编程和配置模型。它的核心特性是依赖注入...
《尚学堂全文检索系统——深入理解Lucene》 在IT领域,搜索引擎技术是不可或缺的一部分,尤其是在大数据时代,如何高效地从海量信息中快速找到所需内容成为了一项挑战。Java平台上的Lucene,作为一款强大的开源全文...
接着,"008_尚学堂马士兵_Java视频教程_Hibernate3.3.2_常见OR框架简介.avi"中,马士兵老师会对比分析其他的ORM框架,如iBatis等,帮助你了解它们与Hibernate的区别和联系,进一步巩固对Hibernate的理解。...
尚学堂科技提供了相关的视频教程,如"张志宇_SERVLET_JSP_视频教程",还有JDBC、MySQL和Tomcat的使用,这些是构建Web应用的基础。通过"JAVA_网上商城项目视频讲解"这样的实战教程,可以提高实际操作能力。 深入J2EE...
尚学堂Java笔记.pdf 本资源主要讲述Java语言的基础知识和应用,包括J2SDK、JRE、JDK、classpath、path、Java应用程序、递归调用等内容。 一、J2SDK和JRE Java2 Software Development Kit(J2SDK)是Java开发需要...
441769547180350是男人就撑20秒【尚学堂】.zip
1. **数据分析**:通过对大量数据的分析,帮助企业做出更明智的决策。 2. **搜索引擎**:Hadoop可以用来索引和搜索互联网上的大量网页数据。 3. **推荐系统**:通过分析用户的浏览历史和购买记录,向用户推荐可能感...
- **尚学堂科技_张志宇_SERVLET_JSP_视频教程_第一版**:介绍了Servlet和JSP的基础知识,这是构建动态网页的基础。 - **尚学堂科技_马士兵_JAVA_系列视频教程_BBS_2007**:通过实际案例,让学员了解如何运用所学知识...
本例子通過學習“002_尚学堂马士兵_Java视频教程_Hibernate3.3.2_HelloWorld.avi”教程實踐的小例子。 整理出来的精简JAR包,共八个。分别: antlr-2.7.6.jar、commons-collections-3.1.jar、dom4j-1.6.1.jar、...
在“hibernate资料5”中,我们有两个关键的学习资源:016_尚学堂马士兵_Java视频教程_Hibernate3.3.2_hibernate基础配置.avi 和 017_尚学堂马士兵_Java视频教程_Hibernate3.3.2_Annotation字段映射位置.avi。...