分享
 
 
 

J2EE Web组件中中文及相关的问题(一)

王朝java/jsp·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

“与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-1,ISO8859-2之类的一系列字符集,它们都是8位的字符集,0~0x7F仍与ASCII字符集保持兼容,大于0x7F的是各种拉丁字符或欧洲字符的扩展。

2. GB2312字符集

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

3. Unicode字符集

Unicode字符最初是16位的(出于需要,后来增加了代用对),它和7位的US-ASCII保持兼容,MS的Windows NT/2000/XP和Sun的Java都用它作为默认的字符集,它最初是美国商务联盟的事实上的标准,它遵循国际通用字符(UCS)集标准:ISO/IEC 10646。Unicode的主要目标是提供一个“通用字符集”,这个通用字符集包括世界上所有的语言,字母和文字,所以在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.org,UTF-8和UTF-16分别定义在IETF的RFC 2279和RFC 2781中,可以通过http://www.ietf.org/rfc2279.txthttp://www.ietf.org/rfc2781.txt访问它们。

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

二、 乱码带来的尴尬

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内码是件很容易的事,Java和JavaScrtipt的字符都是用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 JavaScript在IE6.0中输出

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

<%-- discomfiture1.jsp --%>

<%

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

System.out.println(str);

out.println(str);

%>

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

图 2-3 JSP在IE6.0中出现了乱码

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

因为直接使用的Unicode码生成的字符串,保证了在JSP生成的Servlet discomfiture1$jsp中,字符串str的值一定是“我是中国人”,而服务器窗口的输出也证实了这一点,那么也就是说在Servlet discomfiture$jsp中str的值并不是“我是中国人”,因为它在服务器窗口中输出了乱码,如图 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。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有