首 页 | 新 闻 | 技术中心 | 第二书店 | 《程序员》 | 《开发高手》 | 社 区 | 黄 页 | 人 才
移 动专 题SUNIBM微 软微 创精 华Donews人 邮
我的技术中心 
我的分类 我的文档
全部文章 发表文章
专栏管理 使用说明



 RSS 订阅 
最新文档列表
Windows/.NET
.NET  (rss)    
Visual C++  (rss)    
Delphi  (rss)    
Visual Basic  (rss)    
ASP  (rss)    
JavaScript  (rss)    
Java/Linux
Java  (rss)    
Perl  (rss)    
综合
其他开发语言  (rss)    
文件格式  (rss)    
企业开发
游戏开发  (rss)    
网站制作技术  (rss)    
数据库
数据库开发  (rss)    
软件工程
其他  (rss)    

积极原创作者 
wangchinaking (57)
yjz0065 (113)
coofucoo (105)
Drate (69)
lphpc (30)
smallnest (61)
iiprogram (64)
downmoon (32)
danny_xcz (49)
btbtd (81)
CSDN - 文档中心 - Java 阅读:8969   评论: 13    参与评论
标题   J2EE Web组件中中文及相关的问题(一)     选择自 whodsow 的 Blog
关键字   :Java、JSP、Servlet、中文、非英文字符、乱码、字符集、Unicode、ISO8859-1、GBK、GB2312、Big5、UTF-8、编码(Encode, Encoding)、解码(Decode, Decoding)、charset、JavaScript、Content-Type、过滤器、国际化(International)
出处  

“与C/C++不同,Java中的字符数据是16位无符号型数据,它表示Unicode集,而不仅仅是ASCII集”。这是一个很好的做法,它解决了www上更多的程序设计问题,比如说低成本的国际化(International),然而用16位的字符,却带来了浪费,毕竟Java所处理的信息,绝大多数都是英文,对它们来说7位的ACSII码已经足够了,而Unicode却需要双倍的空间,所以Java的这种兼顾各种语言的做法却是与存储资源及效率的妥协。而对中国的Java程序员(特别是初级的)来说,Java采用Unicode字符,却给我们带来了尴尬甚至噩梦——Web页面上显示的不是中文,而是乱码。

一、        常见字符集简介

    字符集就是字符内码到字符的表现形式之间的映射的集合。ASCII字符A是就内码0x41的表现形式,所以在很多程序语言中,字符变量和整型变量仅在一念之差。

1.        ISO8859系列

ISO8859包括诸如ISO8859-1ISO8859-2之类的一系列字符集,它们都是8位的字符集,0~0x7F仍与ASCII字符集保持兼容,大于0x7F的是各种拉丁字符或欧洲字符的扩展。

2.        GB2312字符集

如果像ISO8859系列一样,大于0x7F的字符用来表示汉字,则最多表示128个,这显然不够,于是就有了GB2312标准所产生的字符集,如果当前字节(8 bit)小于0X80,则仍当它为英文字符;如果它大于等于0x80,则它和紧接着它的下一个字节构成一个汉字字符,这样,GB2312字符集可包含大约4000多个常用简体汉字和其他汉字中的特殊符号(如①㈠之类)。其他类似的汉字字符集还有GBKGB2312的扩展),GB18030Big5(繁,台湾省用),详细规范介绍可参考:http://www.unihan.com.cn/cjk/ana17.htm

3.        Unicode字符集

Unicode字符最初是16位的(出于需要,后来增加了代用对),它和7位的US-ASCII保持兼容,MSWindows NT/2000/XPSunJava都用它作为默认的字符集,它最初是美国商务联盟的事实上的标准,它遵循国际通用字符(UCS)集标准:ISO/IEC 10646Unicode的主要目标是提供一个“通用字符集”,这个通用字符集包括世界上所有的语言,字母和文字,所以在Unicode字符集中,不光“I”是字母,“我”也是字母,在写Java时也可以“int 我是中国人 = 0xff;”。毕竟16位的Unicode字符集最多只有216= 65536个字符,还不足以在实际应用中表示所有的字符,而且在以英文为主要信息的互联网时代,它的使用、存储与传输,都极其浪费空间,所以在此基础上出现了UTF-8(Unicode Transformation Form 8-bit form)UTF-16这两种对Unicode字符编码的规范,在UTF-8中,属于US-ASCII中的字符,仍用一个字节表示,且和US-ASCII兼容,编码其他的字符,则用1(大于0x7F部分)到3个字节。UTF-8的变长性和复杂性,对非ASCII的字符,就不大友好了,也开始违背了Unicode的初衷。而UTF-16则是很简单的编码方式,它完全遵循Unicode标准,用16位的定长空间来表示部分Unicode字符集。关于Unicode的更多规范,请访问Unicode联盟站点:http://www.unicode.orgUTF-8UTF-16分别定义在IETFRFC 2279RFC 2781中,可以通过http://www.ietf.org/rfc2279.txthttp://www.ietf.org/rfc2781.txt访问它们。

一般情况下,字符集名称是大小写不敏感的,所以GB2312也可以写作gb2312Gb2312

二、        乱码带来的尴尬

1.        先看一个JSP

JSP(Java Server Page)的实质还是一个Servlet,所以用JSP,也可以说明Servlet中的一些问题,就一般而言,JSP代码比Servlet代码还要简单。

我们先用JSP来做一个实验,下面的这个JSP文件中含有常量字符串“我是中国人”,看看它在浏览器的输出是否会是乱码?

<%-- discomfiture.jsp --%>

<%

 String str = "我是中国人";

 System.out.println(str);

 out.println(str);

%>

从浏览器打开它,并没有乱码,显示的就是“我是中国人”这个字符串。先别乐,再看看服务器的输出窗口吧,如图2-1,服务器监视窗口输出了乱码(红色下划线标出)。

2-1 服务器窗口中输出的乱码

    虽说只是在服务器端出现了乱码,而客户端浏览器是完全正确显示的,但这里很明显出了什么问题,否则都两边应该是正确的输出。

服务器端输出了乱码,说明服务器Java虚拟机(Java Virtual Machine, JVM)没有“得到”正确的字符串。为了保证JVM能够正确“得到”我们指出的含中文的常量字符串,我们可以直接用字符的Unicode内码代替字符串中的字符,就像

String str=”I am Chinese”;

String str = “\u0049\u0020\u0061\u006D\u0020\u0043\u0068\u006E\u0065\u0073\u0065”;

代替一样。明确给JVM指出这些字符串,是否还会出现乱码呢?要得到一个字符的Unicode内码是件很容易的事,JavaJavaScrtipt的字符都是用Unicode字符集的。先看看输出Unicode字符内码的Java程序:

public class getCode

{

                   public static void main(String args[])

                   {

                            char chs[] = args[0].toCharArray();

                            for(int i = 0; i < chs.length; i++){

                                      System.out.println(chs[i] + " = " + (int)chs[i]);

                            }

                            System.out.println(args[0]);

                   }

}

编译并执行它,结果如图:

2-2 JVM输出

不过JavaScript用起来,怎么也比Java来得快,这里也介绍一段JavaScript代码:

<script>

var str = "我是中国人";

for(var i = 0; i < str.length; i++)

{

          document.wirte(str.charAt(i) + " = " + str.charCodeAt(i) + "<br>");

}

document.write(str);

</script>

保存为HTML文件,输出如下图:

2-2 JavaScriptIE6.0中输出

现在替换discomfiture.jsp中的中文字符串:

<%-- discomfiture1.jsp --%>

<%

 String str = "\u6211\u662F\u4E2D\u56FD\u4EBA";

 System.out.println(str);

 out.println(str);

%>

    实验得到了希望的结果,服务器端输出窗口正确输出了字符串,可客户端浏览器却又输出了乱码,如下图:

2-3 JSPIE6.0中出现了乱码

2-4 客户端刷新两次的服务器窗口的输出

因为直接使用的Unicode码生成的字符串,保证了在JSP生成的Servlet discomfiture1$jsp中,字符串str的值一定是“我是中国人”,而服务器窗口的输出也证实了这一点,那么也就是说在Servlet discomfiture$jspstr的值并不是“我是中国人”,因为它在服务器窗口中输出了乱码,如图 2-1可以发现,当时输出了10个字符,即str的长度是10,并不是5。可为什么在浏览器却好好地得到了这个字符的输出呢? 先简要明白两个概念:编码与解码。

2.        编码与解码

编码(Encode)和解码(Decode)是两个相反的动作。编码是把字符按照某种映射标准(字符集),转换成字节,这时我们把执行编码动作时所采用的标准叫编码(encoding)。如我们对Unicode字符串

我是中国人

按照GB2312标准编码(byte bsg[] = ”我是中国人”.getBytes(“GB2312”);),就可以得到一个字节序列(bytes sequence),用十六进制的码值表示:

0xCE0xD20xCA0xC70xD60xD00xB90xFA0xC80xCB

按照UTF-8标准编码(byte bsu[] = ”我是中国人”.getBytes(“UTF-8”);),就可以得到字节序列:

0xE60x880x910xE60x980xAF0xE40xB80xAD0xE50x9B0xBD0xE40xBA0xBA

而解码则是将字节序列按照某种字符标准(解码,decoding),转换成字符串。如我们对字节序列:

0xCE0xD20xCA0xC70xD60xD00xB90xFA0xC80xCB

按照GB2312解码(new String(bsg,”GB2312”),或对字节序列:

0xE60x880x910xE60x980xAF0xE40xB80xAD0xE50x9B0xBD0xE40xBA0xBA

按照UTF-8解码(new String(bsu,”UTF-8”),均可得到字符串我是中国人,但是如果我们对GB2312编码的字节序列用UTF-8解码,这就乱了套,所得的字符串明显是错误的乱码。

让我们来看一个试验。

import java.io.UnsupportedEncodingException;

public class u2g

{

         public static void main(String args[])throws UnsupportedEncodingException

         {

                   String str = args[0];

                   char chs[] = str.toCharArray();

                   System.out.println("Unicode characters:");

                   for(int i = 0; i < chs.length; i++)

                            System.out.print(chs[i] + " = " + (int)chs[i] + ";");

                   System.out.println();

                   String messages[] = {

                            "Encodes this String into a sequence of bytes using the" +

                            "\nplatform's default charset.",

                            "Encodes this String into a sequence of bytes using gb2312.",

                            "Encodes this String into a sequence of bytes using utf-8."};

                   String encodings[] = {null,"gb2312","utf-8"};

                   byte bs[][] = new byte[3][];

                   for(int h = 0; h < messages.length; h++){

                            System.out.print(messages[h]);

                            if(encodings[h] == null)bs[h] = str.getBytes();

                            else bs[h] = str.getBytes(encodings[h]);

                            for(int l = 0; l < bs[h].length; l++){

                                     if(l % 4 == 0)System.out.println();

                                     System.out.print("byte[" + l + "] = " +

                                                                            Integer.toHexString(bs[h][l] & 0xff) + ";");

                            }

                            System.out.println();

                   }

                   System.out.println("Decodes the sequence of bytes using corresponding encoding.");

                   for(int i = 0; i < bs.length; i++){

                            if(encodings[i] == null)System.out.println(new String(bs[i]));

                            else System.out.println(new String(bs[i], encodings[i]));

                   }

                   String messages1[] = {

                            "Decodes the sequence of bytes encoded by gb2312 into a string\nusing utf-8.",

                            "Decodes the sequence of bytes encoded by utf-8 into a string\nusing gb2312."};

                   for(int h = 0; h < 2; h ++){

                            System.out.println(messages1[h]);

                            str = new String(bs[h+1], encodings[h == 0 ? 2 : 1]);

                            chs = str.toCharArray();

                            System.out.print("Unicode characters:");

                            for(int i = 0; i < chs.length; i++)

                            {

                                     if(i % 4 == 0)System.out.println();

                                     System.out.print(chs[i] + " = " + (int)chs[i] + ";");

                            }

                            System.out.println();

                   }

                   System.out.println("The default encoding of system is " +

                                                           System.getProperty("file.encoding"));

         }

}

JVM输出如图 2-5所示,很明显,对用UTF-8编码的字节流,用GB2312编码是彻底失败了,我们什么字符也没得到。我所使用的系统是MS Windows 2000 Server,默认字符集是GBK,这个实验也可以看出GBK兼容GB2312

相关文章
对该文的评论
CSDN 网友 ( 2004-09-01)
好,不过"对用UTF-8编码的字节流,用GB2312编码是彻底失败了"这句话应该是"对用UTF-8编码的字节流,用GB2312解码是彻底失败了".
CSDN 网友 ( 2004-07-30)
分析得全面深刻。同时,对java及unicode给中文处理带来的问题,评价得也比较中肯。本人对作者-淡如水深表敬意!
jacky334625 ( 2004-05-27)
看了,但是我还是想知道怎么处理了?
yiyuan ( 2004-05-16)
确实是好文,支持
dennis03 ( 2004-02-23)
分析透彻全面,我顶!