- 浏览: 1650184 次
- 性别:
文章分类
- 全部博客 (2929)
- 非技术 (18)
- Eclipse (11)
- JAVA (31)
- 正则表达式 (0)
- J2EE (4)
- DOS命令 (2)
- WEB前端 (52)
- JavaScript (69)
- 数据库 (8)
- 设计模式 (0)
- JFreechart (1)
- 操作系统 (1)
- 互联网 (10)
- EasyMock (1)
- jQuery (5)
- Struts2 (12)
- Spring (24)
- 浏览器 (16)
- OGNL (1)
- WebService (12)
- OSGi (14)
- 软件 (10)
- Tomcat (2)
- Ext (3)
- SiteMesh (2)
- 开源软件 (2)
- Hibernate (2)
- Quartz (6)
- iBatis (2)
最新评论
本章从实际的中文问题中,分析问题的根本原因,以及解决之道。
注意,本章虽然着重说明 “ 中文问题 ” ,但本章所推出的结论却是适合于世界所有语言文字的。
概述
我们在实际开发中碰到的中文问题,真是形形色色,无法一一列举。但是它们不是随机产生的,而是有规律可循,有办法解决的。
我们碰到最多的中文问题,都发生在使用 Java Servlet 写 WEB 应用时。其次,使用 Java Mail API 发送 e-mail 也会有类似的问题。从表象上区分,大致上有以下几种:
- 好端端的中文显示成了问号 “?” ,且一个中文变成 2 个问号。
- 好端端的中文显示成了问号 “?” ,且一个中文变成 1 个问号。
- 好端端的中文显示成了看不懂的符号,如 “ÎÒ°®Alibaba” 。
- WEB 页面中部分中文显示正常,部分中文是乱码。
要分析这些问题的根本原因,首先要了解这些 中文字符的输入源 ,其次是了解这些字符被输出到用户浏览器经过了哪些 转换和输出环节 。中文字符可以来源于:
- 程序内嵌的中文,我们在程序里直接书写中文字符串。
- 文本文件,利用 FileInputStream 读入文件内容并转换成字符。
- XML 文件,利用 XML 解析器读入内存。
- 数据库,利用 SQL 查询,取得的结果。
- 模板文件,例如 Velocity 或 WebMacro 模板,我们使用模板生成 WEB 页面。
- JSP 页面,在 JSP 生成的 WEB 页面。
- 用户通过浏览器提交的表单。
中文字符被装入内存以后,还要经过若干个转换和输出环节,最后才能到达用户的浏览器被用户看到。
- 文本通过 response.getWriter() 输出到浏览器端。
- 浏览器读取服务器的 HTTP 响应,并将响应中包含的 HTML 页面显示在浏览器上。
- 文本可能被写入 XML 文件、文本文件、数据库中。
以上列举的任何一个环节发生错误,都可能产生 “ 乱码 ” 现象。因此发生乱码现象时,不要慌,想想这个乱码的文本是从哪里来的,又是以什么方式输出的。
字符的输入、转换、输出环节
内嵌在程序代码中的中文
因为 Java 源代码( .java )本身是一个文本文件,所以和读普通文本文件一样,编译器( javac )必须以字节流的方式读入文件内容,并以适当的编码转换成 Unicode 字符而存储在 Java 字节码文件( .class )中。例如: Java 源代码文件中包含 GBK 编码的中文字符,则使用下面的命令编译:
- javac-encodingGBKMyClass.java
如果不指定
-encoding 参数, javac 会使用系统默认的编码:在中文 Windows 上,默认是 GBK ,在英文 Linux 上,默认是 ISO-8859-1 。因此,如果文件是在英文 Linux 下编译而未指定 -encoding ,那么文件中的中文 “ 我爱 Alibaba” 就会变成 “ÎÒ°®Alibaba” 了。
从文本文件中读入的字符
正如前面的 TestDecoding 例子 所示,在读入文本文件时,需要指定正确的编码。如果不指明编码,那么 Java 就会使用系统默认的编码来转换文件中的字节流。下列代码往往会产生问题:
-
public
static
StringreadStringFromFile(Stringfilename)
throws
IOException{
-
-
//正确的方法:
-
//FileInputStreamistream=newFileInputStream(filename);
-
//InputStreamReaderreader=newInputStreamReader(istream,charset);
-
-
//可能会导致乱码的方法(取决于运行平台的默认编码):
-
FileReaderreader=
new
FileReader(filename);
-
...
-
}
-
从XML 文件中读入的字符
XML 标准极为严格地遵守 Unicode 标准。 XML 文件的字符编码是定义在 XML 文件中,而不是定义在 XML 解析器中的。如果不明确指定,任何标准的 XML 解析总是以 UTF-8 的方式解码 XML 文件。可以用下面的方式在 XML 文件中指定字符编码:
在解码过程中,如果
注意
注意, XML 规范并没有定义 GBK 和 GB18030 编码,因此不能在 XML 文件使用这两种编码。目前可以使用的中文编码是 GB2312 和 BIG5 。相信这种情况以后会改变。如果确实想使用中文大字符集,请指定 UTF-8 作为 XML 文件的编码。
数据库
首先,数据库一般都可以设置以何种字符编码方式存储文本;其次,数据库的客户端 —— JDBC 驱动 —— 必须设置成和数据库的内置字符编码一致;最后,尽可能使用 UTF-8 存取文本数据,因为这样可以在数据库中方便地存储所有国家的文字。
注意
我们 Alibaba 的 Oracle 数据库目前采用 7 位 ASCII 码存储文本(包括中文),这是一个极大的错误,已经导致了很多问题。我们后面会讲到。
模板文件
Velocity 和 WebMacro 是常用的 Java 模板系统。模板文件也是简单的文本文件。 Velocity 和 WebMacro 都可以在各自的配置文件定义读取模板所用的字符编码方式。例如 Velocity 可以这样设置:
如果是在 这样
JSP 页面
JSP 是一种特殊的 WEB 页面,在第一次使用时,被自动编译成一个普通的 servlet 。在 JSP 的开头指定 JSP 的字符编码:
上面这行告诉
- 用 javac -encoding GBK 命令选项来编译 JSP 所生成的 servlet 源代码。
2. 使用 GBK 编码输出 JSP servlet 中的所有字符,相当于:
3.
-
request.setCharacterEncoding(
"GBK"
)
用户通过浏览器提交的表单
- request.setCharacterEncoding( "GBK" )
浏览器是根据页面的 content type 来决定以何种方式来编码用户输入的表单的。例如,一个页面的 content type 是 text/html; charset=GBK ,那么,当用户按下页面中的 submit 按钮时,浏览器自动将用户的输入用 GBK 方式编码并发送回服务器端。服务器接到用户的请求后,需要用正确的方式来解码,方法是:
然后再调用
注意
必须在第一次调用 request.getParameter(parameterName) 之前调用 request.setCharacterEncoding(charset) ,因为解码是在第一次调用 request.getParameter(parameterName) 时发生的。
Servlet 规范规定,如果没有设定 request.setCharacterEncoding ,则使用 ISO-8859-1 来解码用户输入的表单,而 不是使用系统默认的编码 。
对于 multipart form (例如,上传图片的 form 表单),情况要复杂一些。因为 servlet 并没有直接支持 multipart form 。所以大多数应用程序使用了第三方的工具包来解析 multipart form ,例如: Oreilly COS工具包 。然而,这些工具包大多使用系统默认的编码来解析用户表单,和 servlet 规范不一致。如果你的 servlet 代码没有特别指明编码方式,则两种 form 表单将有不同的表现,必有一种情况会出现乱码现象。
Servlet 输出
Servlet 可以用两种方式向浏览器输出内容:
- 字节流方式 —— 输出到 response.getOutputStream() 。一般用来输出二进制内容,例如图片。
- 字符流方式 —— 输出到 response.getWriter() 。用来输出文本类型的内容,如 HTML 和纯文本。
在此我们只讨论输出文本的情形: response.getWriter() 。在调用 response.getWriter() 前,我们必须设置 content type :
response.getWriter()
在 Turbine 中,在配置文件中指定下面的内容, Turbine 会为你自动设置 content type :
-
locale.
default
.charset=GBK
浏览器如何确定页面的字符编码
- locale. default .charset=GBK
浏览器收到从 WEB 服务器返回的页面时,
- 首先检查 HTTP 响应中指定的 content type ,也就是 servlet 通过 response.setContentType 方法设置的值。如果 content type 中指定字符编码(例如 text/html; charset=GBK ),则使用这种方式解码这个页面。
2. 如果 HTTP 响应中没有指定字符集,那么浏览器会检查 HTML 页面中是否包含:
如果找到,则使用这里指定的字符编码。
- 如果既没有在 HTTP 响应中指定字符编码,也没有在 HTML 内容中指定字符编码,则浏览器根据一定的规则自动确定页面的字符编码。例如,在英文环境中,浏览器会使用 ISO-8859-1 ,简体中文环境中,则使用 GBK 。用户也可以根据自己的需要手工改变这一设置。
其它输出环节
文本还可能被写入 XML 文件、文本文件、数据库中。类似的,输出文件时一般都要指定字符编码。如果不指定,通常 Java 会选择系统默认的编码。这为程序运行的结果产生了不确定因素。
“ 乱码 ” 分析
明白了各输入、转换、输出环节是怎样工作的,我们的分析工作就有头绪了。在深入分析之前,有不少情况,观察乱码的表面现象就可以得到大概的结论。
一个中文变成了两个问号 “?”
这个现象通常表明字符在输入时出错,也就是解码错误。
虽然输出编码是对的,但在此之前,由于错误的输入编码,每个中文字变成了两个不相干的欧洲字符。而这些欧洲字符的编码和 GBK 编码是相冲突的(但也不一定完全冲突,例如上例中的第三个字节 B0 ,被转换成 GBK 的 E3A1 )。因此大部分中文被输出成两个问号。
如果出现乱码的中文字是从 Velocity 模板读入的,说明 Velocity 配置文件中的 input.encoding 设置不正确;如果这个中文字是从数据库读入的,说明数据库的配置出错,也有可能文本在保存进数据库之前就已经错了;如果这个中文字是从用户表单输入的,很可能是你忘了调用 request.setCharacterEncoding("GBK") 。
一个中文变成了一个问号 “?”
这个现象通常表明字符在输出时出错,也就是编码错误。
这很可能是因为没有设置 response.setContentType("text/html; charset=GBK") 。
中文显示成了看不懂的符号,如 “ÎÒ°®Alibaba”
这个现象通常表明字符在输入输出时都出错了。
明眼人一看就发现,实际上在这种情况下,最后输出到浏览器上的 字节流是正确的 !只是因为 content type 被设成了错误的 ISO-8859-1 编码,所以才导致浏览器显示不正确的。事实上,用户可以手工改变浏览器的设置,使浏览器使用 GBK 对字节流重新解码。
看起来象是数学中的 “ 负负得正 ” 。为什么会这样呢?这是因为 ISO-8859-1 编码的特殊性导致的。 ISO-8859-1 字符集的编码范围是 0000-00FF ,正好和一个字节的编码范围相对应。这种特性保证了使用 ISO-8859-1 进行编码 / 解码可以保持 编码数值 “ 不变 ” 。虽然中文字符在输入 JVM 时,被错误地 “ 拆 ” 成了两个欧洲字符,但由于输出时也是用 ISO-8859-1 ,结果被 “ 拆 ” 开的中文字的两半又被神奇地合并在一起。
这种情形在英文版的 Linux 上最常发生,事实上我们公司的很多程序就是这样做的。英文版 Linux 的系统默认编码为 ISO-8859-1 。假设我们的 servlet 从模板中生成动态网页:
- 输入环节 —— 如果我们不指定模板系统的字符编码,那么, Java 会使用系统默认的编码( ISO-8859-1 )读入模板文件,从而将一个 GBK 中文编码看作两个欧洲字符。
- 输出环节 —— 如果我们在设置 response.setContentType("text/html") 时不指明 “ charset ” ,按 servlet 规范,系统应以 ISO-8859-1 输出页面。从而恢复了正确的字节流。
- 浏览器输入环节 —— 浏览器发现 HTTP 响应中未指定字符编码,则检查 HTML 中有没有 <meta http-equiv="Content-Type" content="text/html; charset=GBK"> 之类的标记,如果有,则以 GBK 显示页面。此时页面显示是完全正常的(没有乱码)。
- 如果浏览器发现 HTTP 响应中未指定字符编码,并且 HTML 中也没有定义 meta 标记,则使用系统默认的编码。这取决于运行浏览器的平台和浏览器的设置。一般英文平台会以 ISO-8859-1 显示页面,从而显示成乱码。中文平台有可能可以正确显示页面。
同样的代码,如果在中文 Windows 上运行,因为系统默认编码为 GBK ,因而会转变成 “一个中文变成一个问号 ” 的情形。
如果页面中有部分字符不是来源于模板,而是来源于 XML 文件或 UTF-8 编码的数据库,又会转变成 “WEB页面中部分中文显示正常,部分中文是乱码 ” 的情形。
此外,把一个中文字符转换成两个欧洲字符,不仅使字符串变长了一倍,影响效率,而且前面所说的和 Unicode 相关的功能一概失效:断句断词、排序、查看字符属性、格式化日期和数字。
可见,使用这种方法显示中文,引入了诸多不确定因素,实在不是一种可取的方法。但是很多程序员满足于 “ 完成任务 ” ,却不求甚解,不理解 Unicode 的精义。甚至网上很多的文章也主张这么做,真是可悲可叹。
WEB 页面中部分中文显示正常,部分中文是乱码
很明显,这是由于同一页面中的字符是从不同的输入源取得的。假设有如下常见情形:
- 从模板取得的中文字使用了系统默认编码,中文 Windows 上是 GBK ,英文 Linux 上是 ISO-8859-1 ,后者将一个中文转变成了两个欧洲字符。
- 从 XML 取得的中文字总是正确的 Unicode 字符。
- 从用户表单读入的中文字,如果不指定编码( request.setCharacterEncoding ),总是以 ISO-8859-1 解码。
结果就是:
Content Type |
浏览器环境 |
服务器环境 |
从模板取得的中文字 |
从 XML 取得的中文字 |
从用户表单取得的中文字 |
text/html |
中文 Windows |
中文 Windows |
一个中文变成 1 个问号 |
一个中文变成 1 个问号 |
碰巧正常 |
英文 Linux |
碰巧正常 |
一个中文变成 1 个问号 |
碰巧正常 |
||
英文 Windows |
中文 Windows |
一个中文变成 1 个问号 |
一个中文变成 1 个问号 |
中文变成欧洲字符 |
|
英文 Linux |
中文变成欧洲字符 |
一个中文变成 1 个问号 |
中文变成欧洲字符 |
||
text/html; charset=GBK |
任意 |
中文 Windows |
正常 |
正常 |
一个中文变成 2 个问号 |
英文 Linux |
一个中文变成 2 个问号 |
正常 |
一个中文变成 2 个问号 |
注意
所谓 “ 碰巧正常 ” 是指虽然在服务器上,一个中文被当作两个欧洲字符处理,但是输出到浏览器以后,又被重新组合成了正确的字节序列,并且浏览器按默认的选项,会以中文 GBK 解码此序列。对于 “ 中文变成欧洲字符 ” 的情形,可以在浏览器上人工设置字符编码为 GBK ,或是在 HTML 中设置 <meta http-equiv="Content-Type" content="text/html; charset=GBK"> 标记,来显示出中文。
深入分析
以上只是分析了最常见的 “ 乱码 ” 现象。实际上,还可能会发生更复杂一点的情形。但是无论什么情形,都可以通过仔细分析中文字符经过的每一个 输入 、 转换 、 输出 环节,来了解它的原因。
发表评论
-
中文化和国际化问题权威解析之一:字符编码发展历程
2009-08-11 20:06 808中文化和国际化问题权威解析之一:字 ... -
中文化和国际化问题权威解析之二:Java国际化基础
2009-08-11 20:07 716我们知道 Unicode 为国际化( I18n ... -
中文化和国际化问题权威解析之五:URL编码/Misc
2009-08-11 20:09 615通过前面中文化、国际化问题解决的系列1-4,相信大家对字符集、 ... -
中文化和国际化问题权威解析之四:Java中文化和国际化攻略
2009-08-11 20:09 669一般攻略 既然在 Java 内部是直接使用 U ... -
中文化和国际化问题权威解析之七:JS中的escape、encodeURI、encodeURIComponent解惑
2009-08-11 20:10 633面一篇文档《中文化和国际化问题权威解析之五:URL编码/Mis ... -
中文化和国际化问题权威解析之六:MIME编码/字符传输编码
2009-08-11 20:10 624MIME(Multipurpose Internet Ma ... -
通过IP地址和子网掩码与运算计算相关地址
2010-03-12 14:41 503通过IP地址和子网掩码 ... -
子网掩码和IP地址计算网络地址和广播地址的换算
2010-03-12 14:50 1138子网掩码和IP地址计算 ... -
更改google桌面搜索的索引文件位置
2010-11-15 09:14 672Google Desktop Search默认的索引文件是保存 ...
相关推荐
理解反射有助于实现元编程,例如动态代理、序列化和配置文件的解析。 5. **安全性**:Java的安全模型提供了一套强大的机制来保护代码和数据。本书将介绍如何创建安全的Java应用,包括权限管理、沙箱模型以及证书和...
- **国际化和本地化**: - **字符编码**:支持更多的字符集和编码方式,如UTF-8。 - **本地化支持**:增强了日期时间、数字格式化等功能。 #### 3. Java 1.6 的安装与配置 - **安装步骤**: - 下载官方提供的...
《Thinking in Java》是Java编程领域的一本经典著作,由Bruce Eckel撰写,中文第三版则是这本权威书籍的中文翻译版本。这本书以其深入浅出的讲解方式和丰富的实例,深受程序员们的喜爱,无论是初学者还是有经验的...
《Java指南》是一本深入解析Java编程语言的重要参考资料,它由SUN公司(后被甲骨文公司Oracle收购)官方发布,旨在为开发者提供全面、权威的Java学习资源。中文版则是通过BING翻译引擎将英文原版进行翻译,虽然机器...
4. **基本类型与数据结构**:详细解析整型、浮点型、字符型、布尔型等基本类型,以及数组的创建、初始化和操作。 5. **控制流**:涵盖条件语句(if、switch)、循环(for、while、do-while)、跳转语句(break、...
《Thinking in Java》是Bruce Eckel的经典著作,被誉为学习Java编程的权威指南。第三版针对Java SE 5.0进行了全面更新,包含了Java语言的最新特性。这本书以其深入浅出的讲解方式,全面覆盖了从基础到高级的Java编程...
- JAXB:用于XML和Java对象之间的绑定,方便XML的序列化和反序列化。 10. **Java虚拟机(JVM)**: - 类加载机制:加载、验证、准备、解析和初始化五个阶段。 - 内存模型:堆、栈、方法区、本地方法栈、程序...
│ │ │ Java程序员认证模拟题及详细分析.doc │ │ │ question.rar │ │ │ test4.doc │ │ │ 模拟题.rar │ │ │ 经典的104-147模拟题.rar │ │ │ │ │ ├─035 │ │ │ 2003.10.5.15.51.43.TestKing%...
### HBase权威指南知识点梳理 #### 一、简介与快速开始 ...以上是对《HBase权威指南》中文版文档的主要知识点总结,涵盖了从基础概念、配置部署到高级应用等多个方面,旨在帮助读者全面掌握HBase的相关知识和技术。
- **介绍**:HBase是建立在Apache Hadoop之上的分布式、版本化的列存储数据库,支持高并发读写操作。 - **快速开始**:指导用户如何快速搭建HBase环境。 - **配置**: - **Java环境配置**:确保Java版本兼容HBase的...
《Inside the Java Virtual Machine》(中文第二版)是一本深入探讨Java虚拟机(JVM)的权威著作。这本书详尽地介绍了JVM的工作原理、内存管理、类加载机制以及字节码执行等核心概念,是Java开发者提升技术深度的...
《Maven权威指南中文完整版清晰》是一本深入解析Maven构建工具的教程,它为Java开发者提供了详尽的指导,帮助他们理解和掌握Maven的使用。Maven是Apache软件基金会开发的一个项目管理工具,主要用于Java项目的构建、...
通过以上对Hadoop权威指南中文版中涉及的关键知识点的梳理,我们可以更加全面地理解Hadoop的技术体系及其在大数据领域的应用价值。无论是对于初学者还是有经验的专业人士来说,这些知识点都是非常宝贵的参考资料。
### Maven权威指南中文版知识点概览 #### 一、引言与Maven概念 - **Maven概述**:Maven是一种项目管理和理解工具,旨在通过一套标准流程来简化项目的构建过程。 - **约定优于配置(Convention Over Configuration)...
《Hadoop权威指南》中文版是一本深度解析Hadoop技术的专著,它为读者提供了全面、深入理解Hadoop分布式计算框架的知识体系。Hadoop是由Apache基金会开发的开源项目,其设计目标是处理和存储海量数据,使得企业能够...
《Maven权威指南中文版.pdf》是一本专为Java开发者设计的综合性指南,全面解析了Maven这一强大的项目管理和构建工具。Maven以其约定优于配置的理念,极大地简化了项目的构建过程,使得开发者能够更加专注于代码编写...
《美河提供.Java数据结构和算法中文第二版》是一本深度解析Java环境下数据结构与算法原理及应用的专业书籍。此书由国外计算机科学专家Robert Lafore编写,经过国内技术团队的精心翻译与修订,旨在为中国程序员和...