字体编码学习-Java 编程技术中汉字问题的分析及解决 ---JAVA汉字显示问题的解决方案

字体编码学习-

关键字:USC,Unicode,utf-8,gb2312,字库

什么是编码

为了交流信息,所以对字符进行了统一的编码。

UCS和ISO10646
       ISO10646定义了通用字符集(Universal Character Set,UCS).UCS是国际标准编码,包含了全球

所有字符。UCS使用31bit进行编码。现在只分配了前65534个码位,这个16位的子集称为基本多语言面

(BMP)。

什么Unicode

    Unicode编码是UCS级别3的实现。Unicode编码全码为4个字节,所有字符都使用等的编码方式,现

在只使用两个字节编码。兼容UCS定义的BMP

GB2313与Unicode

       GB2312是中国定制的国际准标编码,由两个字节组成,最高位为1表示汉字,最高为0表示是英

文。
       GB2312与区位码存在数值关系,区位码+20H=GB2312
       GB2312与Unicode的转换不成数学关系,只能通过映射表来实现。
       GB2312为80年定义的基本编码扩展编码有
       GBK
       GB18030
       GB13000与GBK不兼容,只是使用了相同的词汇.
       最新为GB16500-95与unicode3.0兼容
Unicode与UTF-8
       Utf-8是为了兼容软件处理的编码,是unicode的另一种表示方式。使用变化的方式编码,第一

字节表示字符的长度,后面的字符以10开头表示编码。0开头表示ascii编码。

       例如:

Unicode 字符 U+00A9 = 1010 1001 (版权符号) 在 UTF-8 里的编码为
              11000010 10101001 = 0xC2 0xA9

字库与编码关系

       字库是编码表字符显示的描述文件。字符编码是在字库中查询需要显示的字符的索引值。
       不同的编码就应该有不同的字库,比如unicode编码就有unicode的字库。Gb2312编码就有

gb2312的字库。
字库格式

       字库的格式主要有TrueType(ttf),PostScript(rip),OpenType等
       TrueType是微软定义的字库格式,主要用于软件显示这种要求精度不高的环境,
       PostScript是Adobe定义的字库格式,主要用于排版印刷等大字打印精度要求很高的环境。
       OpenType是TrueType与PostScript的综合格式,并且是使用了unicode的编码。
 
字体引擎
       字体引擎用于读取字库,显示文字,文字转换等操作。现在免费的字体引擎有FreeType.

参考文献
    UTF-8 and Unicode FAQ by Markus Kuhn

    http://leuksman.com/linux/TrueType-HOWTO.html

http://www.5ivb.net/Info/55/Info16006/

http://developer.apple.com/fonts/TTRefMan/index.html


 

以前一直对utf、unicode、ascII还有GBK编码方式不太了解,只知道如果有中文的话一般用utf-8或GBK

存储,今天正好又接触到了这个问题就google了下。ASCII是用来表示英文的一种编码规范,表示的最

大字符数为256个,每个字符占1个字节。如果只用来表示英文应该是绰绰有余了,可是还要表示中文、

阿拉伯文所以就有很大的不足了,于是就产生了GB2312。很多人应该对这个比较了解,很多国内网页指

定的编码都是GB2312的,它其实是对ASCII的一种扩展,是每个国家自己制定的编码规范,比如一个中

文字符是由两个扩展ASCII字符表示。但因为GB2312是国家标准所以会有一些问题,记得我们小时候玩

一些繁体游戏时需要借助一些南极星之类的软件转换编码吗?因为台湾很多用的都是big5编码,他和

GB2312的编码格式还是类似的,会显示出一些奇怪的文字或是偶尔也会有个别汉字。后来因为GB2312所

包含的汉字太少了,所以又扩展出来GBK编码。GBK包括了大部分的汉字,并且还加入了big5中几乎所有

的繁体字体(但big5和GBK中的繁体字体并不兼容)。之后还有GB18030编码,其实主要还是字符集的变

化。ASCII—GB2312—GBK—GB18030他们都是向下兼容的,区分英文编码和中文编码的方法是高字节的

最高位不为0,其实GB中文编码都是双字节字符集。
因为GB编码都是国家标准,所以如果要解决中文问题不能从扩展ASCII角度入手了,于是出现了unicode

和utf。unicode分为UCS-2、UCS-4,目前常用的是UCS-2是用2个字节为字符编码,可以表示的数为

2^16=65535,基本可以表示欧美和大部分亚洲汉字,并且因为UCS-2是双字节的所以每个汉字或英文都

是由1个unicode构成,那拆字和统计字数比ASCII方便了很多。似乎unicode是比较完美了,可是它却有

一个很致命的缺点,就是并不能和ASCII兼容。ASCII字符是单个字节的,比如"A"的ASCII是65。而

Unicode是双字节的,比如"A"的Unicode是0065,这就造成了一个非常大的问题:以前处理ASCII的那套

机制不能被用来处理Unicode了 。另一个更加严重的问题是,C语言使用'/0'作为字符串结尾,而

Unicode里恰恰有很多字符都有一个字节为0,这样一来,C语言的字符串函数将无法正常处理Unicode,

除非把世界上所有用C写的程序以及他们所用的函数库全部换掉 。
于是出现了utf,它是将Unicode编码规则和计算机的实际编码对应起来的一个规则。现在流行的UTF有2

种:UTF-8和UTF-16。UTF-8是以8位为单元对UCS进行编码,它定义了一种"区间规则",这种规则可以和

ASCII编码保持最大程度的兼容 。具体的编码方式大家可以搜索一下,俺也只是一些浅显的了解,如果

有不对的地方请大家指出来~~~~
/

Unicode:

unicode.org制定的编码机制, 要将全世界常用文字都函括进去.
在1.0中是16位编码, 由U+0000到U+FFFF. 每个2byte码对应一个字符; 在2.0开始抛弃了16位限制, 原

来的16位作为基本位平面, 另外增加了16个位平面, 相当于20位编码, 编码范围0到0x10FFFF.

UCS:

ISO制定的ISO10646标准所定义的 Universal Character Set, 采用4byte编码.

Unicode与UCS的关系:

ISO与unicode.org是两个不同的组织, 因此最初制定了不同的标准; 但自从unicode2.0开始, unicode

采用了与ISO 10646-1相同的字库和字码, ISO也承诺ISO10646将不会给超出0x10FFFF的UCS-4编码赋值,

使得两者保持一致.

UCS的编码方式:

 

UCS-2, 与unicode的2byte编码基本一样.

UCS-4, 4byte编码, 目前是在UCS-2前加上2个全零的byte.

UTF: Unicode/UCS Transformation Format

UTF-8, 8bit编码, ASCII不作变换, 其他字符做变长编码, 每个字符1-3 byte. 通常作为外码. 有以下

优点:
* 与CPU字节顺序无关, 可以在不同平台之间交流
* 容错能力高, 任何一个字节损坏后, 最多只会导致一个编码码位损失, 不会链锁错误(如GB码错一个

字节就会整行乱码)

UTF-16, 16bit编码, 是变长码, 大致相当于20位编码, 值在0到0x10FFFF之间, 基本上就是unicode编

码的实现. 它是变长码, 与CPU字序有关, 但因为最省空间, 常作为网络传输的外码.
UTF-16是unicode的preferred encoding.

UTF-32, 仅使用了unicode范围(0到0x10FFFF)的32位编码, 相当于UCS-4的子集.

UTF与unicode的关系:

Unicode是一个字符集, 可以看作为内码.
而UTF是一种编码方式, 它的出现是因为unicode不适宜在某些场合直接传输和处理. UTF-16直接就是

unicode编码, 没有变换, 但它包含了0x00在编码内, 头256字节码的第一个byte都是0x00, 在操作系统

(C语言)中有特殊意义, 会引起问题. 采用UTF-8编码对unicode的直接编码作些变换可以避免这问题,

并带来一些优点.

中国国标编码:

GB 13000: 完全等同于ISO 10646-1/Unicode 2.1, 今后也将随ISO 10646/Unicode的标准更改而同步更

改.

GBK: 对GB2312的扩充, 以容纳GB2312字符集范围以外的Unicode 2.1的统一汉字部分, 并且增加了部分

unicode中没有的字符.

GB 18030-2000: 基于GB 13000, 作为Unicode 3.0的GBK扩展版本, 覆盖了所有unicode编码, 地位等同

于UTF-8, UTF-16, 是一种unicode编码形式. 变长编码, 用单字节/双字节/4字节对字符编码. GB18030

向下兼容GB2312/GBK.
GB 18030是中国所有非手持/嵌入式计算机系统的强制实施标准.


 彻底解决程序乱码问题
        由于程序编程过程中存在众多的编码集,而这些编码集又各自有自己的方法来表示一个中文字

符。由此,造成我们的程序显示中文的时候乱码。最多的是本来是中文,但结果显示为问号?

       这篇文章从我碰到过的问题来从根本上解决乱码的问题,反正我用这个方法是百战百胜^_^嘎嘎

       我们知道,在我们的中文系统中使用的是“GBK”编码集。

       也就是说,比如我们从本地系统中读入一个包含中文内容的.txt文件,那么我们从这个磁盘空

间所获得是流文件是个经过“GBK”的。

      但是,假如我们的JVM默认的编码集是“ISO-8859-1”,那么这个流文件经过我们的JVM编码就变

成一个Unicode的字符或者字符串。

       是不是说的很抽象。

       下面我具体解释一下。

       什么是编码?编码说白了就是从字节到字符(Unicode)的过程,因为我们的字符或字符串在JVM

中是用Unicode表示的,也就是说编码实质上是获取一个Unicode码的过程。

      我再来解释一下解码?解码就是把JVM里的Unicode码转换成我们本地字符集所表示的字节流。

     如果在编程过程中,JVM默认的字符集和我们的中文系统,或者数据库文件等采用的字符集不一样

    这样的情况下,如果我们仅仅是编码,而不去解码。也就说把本来用“GBK”的字节流采用“ISO-

8859-1”转换成字符或字符串,这个时候就产生了乱码。

    当然,现在JVM和中文系统默认的编码集已经一样了,所以产生这种情况的很少。但是在读取数据

库文件的时候依然会经常碰到这种情况。所以,我一贯主张从本质上来了解一个知识,然后才能融会贯

通,应用自如,这是高手的境界,当然,我不能算高手^_^哈哈

      好了,如果你现在还不明白编码解码怎么回事?以及乱码产生的原因。我想你可以去撞墙了。

     那么,编码的时候两个系统的字符集不一致,该怎么办呢?怎么样把他们变成一致?

     如果有可以直接改变系统字符集的方法,那当然是最好的了。比如在JVM里

   Properties pps = System.getProperties();

   pps.put("file.encoding","GBK");

   这样,就把JVM的默认字符集改变成“GBK”了。

    那么,如果没有改怎么办?

  什么?没办法?那是不可能滴,没有程序解决不了的问题,这也是偶滴名言,嘎嘎!

当然要再次编码了,或者说重新编码!

  还记得不?刚才我们解码的时候得到一个利用JVM解码的字符或字符串(也可以说Unicode)

 好,如果不记得,麻烦你再看看上面写的,记性太差了!

  我们利用这个字符或字符串再重新编码。

 我们把这个字符或字符串再次现出原形,也就是字节数组。

  byte[] data = str.getBytes("ISO-8859-1");

 好,现在又获得了“ISO-8859-1”字符集下的字节数组。

这个里面就完整的存放着我们想要的汉字,哈哈。

得到了字节数组,不就好办了吗?

还不会?没关系,我教你!

String newstr = new String(data,“GBK”);

好了,得到了我们完整的字符串了!编码变成!

这个字符串就是我们要的中文系统下的字符串,它又经过我们“GBK”编码过了!

可以放心的输出到中文系统下了。

其实说白了,编码解码就是把我们从系统得到的字符转换成字节数组,然后对它用系统字符集加工,加

工完后再重新包装,再把它输出。

//


 Java 编程技术中汉字问题的分析及解决
在基于 Java 语言的编程中,我们经常碰到汉字的处理及显示的问题。一大堆看不懂的
乱码肯定不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Java 语言
默认的编码方式是UNICODE ,而我们中国人通常使用的文件和数据库都是基于 GB2312 
或者 BIG5 等方式编码的,怎样才能够恰当地选择汉字编码方式并正确地处理汉字的编
码呢?本文将从汉字编码的常识入手,结合 Java 编程实例,分析以上两个问题并提出
解决它们的方案。
----------------------------------------------------------------------------
----
现在 Java 编程语言已经广泛应用于互联网世界,早在 Sun 公司开发 Java 语言的时候
,就已经考虑到对非英文字符的支持了。Sun 公司公布的 Java 运行环境(JRE)本身就
分英文版和国际版,但只有国际版才支持非英文字符。不过在 Java 编程语言的应用中
,对中文字符的支持并非如同 Java Soft 的标准规范中所宣称的那样完美,因为中文字
符集不只一个,而且不同的操作系统对中文字符的支持也不尽相同,所以会有许多和汉
字编码处理有关的问题在我们进行应用开发中困扰着我们。有很多关于这些问题的解答
,但都比较琐碎,并不能够满足大家迫切解决问题的愿望,关于 Java 中文问题的系统
研究并不多,本文从汉字编码常识出发,分析 Java 中文问题,希望对大家解决这个问
题有所帮助。
汉字编码的常识
我们知道,英文字符一般是以一个字节来表示的,最常用的编码方法是 ASCII 。但一个
字节最多只能区分256个字符,而汉字成千上万,所以现在都以双字节来表示汉字,为了
能够与英文字符分开,每个字节的最高位一定为1,这样双字节最多可以表示64K格字符
。我们经常碰到的编码方式有 GB2312、BIG5、UNICODE 等。关于具体编码方式的详细资
料,有兴趣的读者可以查阅相关资料。我肤浅谈一下和我们关系密切的 GB2312 和 UNI
CODE。GB2312 码,中华人民共和国国家标准汉字信息交换用编码,是一个由中华人民共
和国国家标准总局发布的关于简化汉字的编码,通行于中国大陆地区及新加坡,简称国
标码。两个字节中,第一个字节(高字节)的值为区号值加32(20H),第二个字节(低
字节)的值为位号值加32(20H),用这两个值来表示一个汉字的编码。UNICODE 码是微
软提出的解决多国字符问题的多字节等长编码,它对英文字符采取前面加“0”字节的策
略实现等长兼容。如 “A” 的 ASCII 码为0x41,UNICODE 就为0x00,0x41。利用特殊
的工具各种编码之间可以互相转换。
Java 中文问题的初步认识
我们基于 Java 编程语言进行应用开发时,不可避免地要处理中文。Java 编程语言默认
的编码方式是 UNICODE,而我们通常使用的数据库及文件都是基于 GB2312 编码的,我
们经常碰到这样的情况:浏览基于 JSP 技术的网站看到的是乱码,文件打开后看到的也
是乱码,被 Java 修改过的数据库的内容在别的场合应用时无法继续正确地提供信息。

String sEnglish = “apple”;
String sChinese = “苹果”;
String s = “苹果 apple ”;
sEnglish 的长度是5,sChinese的长度是4,而 s 默认的长度是14。对于 sEnglish来说
, Java 中的各个类都支持得非常好,肯定能够正确显示。但对于 sChinese 和 s 来说
,虽然 Java Soft 声明 Java 的基本类已经考虑到对多国字符的支持(默认 UNICODE 
编码),但是如果操作系统的默认编码不是 UNICODE ,而是国标码等。从 Java 源代码
到得到正确的结果,要经过 “Java 源代码-> Java 字节码-> ;虚拟机->操作系统->显
示设备”的过程。在上述过程中的每一步骤,我们都必须正确地处理汉字的编码,才能
够使最终的显示结果正确。
“ Java 源代码-> Java 字节码”,标准的 Java 编译器 javac 使用的字符集是系统默
认的字符集,比如在中文 Windows 操作系统上就是 GBK ,而在 Linux 操作系统上就是
ISO-8859-1,所以大家会发现在 Linux 操作系统上编译的类中源文件中的中文字符都出
了问题,解决的办法就是在编译的时候添加 encoding 参数,这样才能够与平台无关。
用法是
javac –encoding GBK。
“ Java 字节码->虚拟机->操作系统”, Java 运行环境 (JRE) 分英文版和国际版,
但只有国际版才支持非英文字符。 Java 开发工具包 (JDK) 肯定支持多国字符,但并
非所有的计算机用户都安装了 JDK 。很多操作系统及应用软件为了能够更好的支持 Ja
va ,都内嵌了 JRE 的国际版本,为自己支持多国字符提供了方便。
“操作系统->显示设备”,对于汉字来说,操作系统必须支持并能够显示它。英文操作
系统如果不搭配特殊的应用软件的话,是肯定不能够显示中文的。
还有一个问题,就是在 Java 编程过程中,对中文字符进行正确的编码转换。例如,向
网页输出中文字符串的时候,不论你是用
out.println(string);       // string 是含中文的字符串
还是用
<%=string%>,都必须作 UNICODE 到 GBK 的转换,或者手动,或者自动。在 JSP 1.0中
,可以定义输出字符集,从而实现内码的自动转换。用法是
<%@page ContentType=”text/html;charset=gb2312” %>
但是在一些 JSP 版本中并没有提供对输出字符集的支持,(例如 JSP 0.92),这就需
要手动编码输出了,方法非常多。最常用的方法是
String s1 = request.getParameter(“keyword”);
String s2 = new String(s1.getBytes(“ISO-8859-1”),”GBK”);
getBytes 方法用于将中文字符以“ISO-8859-1”编码方式转化成字节数组,而“GBK”
 是目标编码方式。我们从以ISO-8859-1方式编码的数据库中读出中文字符串 s1 ,经过
上述转换过程,在支持 GBK 字符集的操作系统和应用软件中就能够正确显示中文字符串
 s2 。
Java 中文问题的表层分析及处理
背景
开发环境
JDK1.15
Vcafe2.0
JPadPro
服务器端
NT IIS
Sybase System
Jconnect(JDBC)
客户端
IE5.0
Pwin98
.CLASS 文件存放在服务器端,由客户端的浏览器运行 APPLET , APPLET 只起调入 FR
AME 类等主程序的作用。界面包括 Textfield ,TextArea,List,Choice 等。
I.       取中文
用 JDBC 执行 SELECT 语句从服务器端读取数据(中文)后,将数据用 APPEND 方法加
到 TextArea(TA) ,不能正确显示。但加到 List 中时,大部分汉字却可正确显示。

将数据按“ISO-8859-1” 编码方式转化为字节数组,再按系统缺省编码方式 (Defaul
t Character Encoding) 转化为 STRING ,即可在 TA 和 List 中正确显示。
程序段如下:
dbstr2 = results.getString(1);
//After reading the result from DB server,converting it to string.
dbbyte1 = dbstr2.getBytes(“iso-8859-1”);
dbstr1 = new String(dbbyte1);
在转换字符串时不采用系统默认编码方式,而直接采用“ GBK” 或者 “GB2312” ,在
 A 和 B 两种情况下,从数据库取数据都没有问题。
II.    写中文到数据库
处理方式与“取中文”相逆,先将 SQL 语句按系统缺省编码方式转化为字节数组,再按
“ISO-8859-1”编码方式转化为 STRING ,最后送去执行,则中文信息可正确写入数据
库。
程序段如下:
sqlstmt = tf_input.getText();
//Before sending statement to DB server,converting it to sql statement.
dbbyte1 = sqlstmt.getBytes();
sqlstmt = newString(dbbyte1,”iso-8859-1”);
_stmt = _con.createStatement();
_stmt.executeUpdate(sqlstmt);
……
问题:如果客户机上存在 CLASSPATH 指向 JDK 的 CLASSES.ZIP 时(称为 A 情况),
上述程序代码可正确执行。但是如果客户机只有浏览器,而没有 JDK 和 CLASSPATH 时
(称为 B 情况),则汉字无法正确转换。
我们的分析:
1.经过测试,在 A 情况下,程序运行时系统的缺省编码方式为 GBK 或者 GB2312 。在
 B 情况下,程序启动时浏览器的 JAVA 控制台中出现如下错误信息:
Can't find resource for sun.awt.windows.awtLocalization_zh_CN
然后系统的缺省编码方式为“8859-1”。
2.如果在转换字符串时不采用系统缺省编码方式,而是直接采用 “GBK” 或“GB2312”
,则在 A 情况下程序仍然可正常运行,在 B 情况下,系统出现错误:
UnsupportedEncodingException。
3.在客户机上,把 JDK 的 CLASSES.ZIP 解压后,放在另一个目录中, CLASSPATH 只包
含该目录。然后一边逐步删除该目录中的 .CLASS 文件,另一边运行测试程序,最后发
现在一千多个 CLASS 文件中,只有一个是必不可少的,该文件是:
sun.io.CharToByteDoubleByte.class。
将该文件拷到服务器端和其它的类放在一起,并在程序的开头 IMPORT 它,在 B 情况下
程序仍然无法正常运行。
4.在 A 情况下,如果在 CLASSPTH 中去掉 sun.io.CharToByteDoubleByte.class ,则
程序运行时测得默认编码方式为“8859-1”,否则为 “GBK” 或 “GB2312” 。
如果 JDK 的版本为1.2以上的话,在 B 情况下遇到的问题得到了很好的解决,测试的步
骤同上,有兴趣的读者可以尝试一下。
[/b]Java 中文问题的根源分析及解决[/b]
在简体中文 MS Windows 98 + JDK 1.3 下,可以用 System.getProperties() 得到 Ja
va 运行环境的一些基本属性,类 PoorChinese 可以帮助我们得到这些属性。
类 PoorChinese 的源代码:
public class PoorChinese {
   public static void main(String[] args) {
       System.getProperties().list(System.out);
   }
}
执行 java PoorChinese 后,我们会得到:
系统变量 file.encoding 的值为 GBK ,user.language 的值为 zh , user.region 的
值为 CN ,这些系统变量的值决定了系统默认的编码方式是 GBK 。
在上述系统中,下面的代码将 GB2312 文件转换成 Big5 文件,它们能够帮助我们理解
 Java 中汉字编码的转化:
import java.io.*;
import java.util.*;
public class gb2big5 {
static int iCharNum=0;
public static void main(String[] args) {
System.out.println("Input GB2312 file, output Big5 file.");
if (args.length!=2) {
System.err.println("Usage: jview gb2big5 gbfile big5file");
System.exit(1);
   }
String inputString = readInput(args[0]);
writeOutput(inputString,args[1]);
System.out.println("Number of Characters in file: "+iCharNum+".");
}
static void writeOutput(String str, String strOutFile) {
try {
FileOutputStream fos = new FileOutputStream(strOutFile);
Writer out = new OutputStreamWriter(fos, "Big5");
out.write(str);
out.close();
}
catch (IOException e) {
e.printStackTrace();
e.printStackTrace();
}
}
static String readInput(String strInFile) {
StringBuffer buffer = new StringBuffer();
try {
FileInputStream fis = new FileInputStream(strInFile);
InputStreamReader isr = new InputStreamReader(fis, "GB2312");
Reader in = new BufferedReader(isr);
int ch;
while ((ch = in.read()) > -1) {
iCharNum += 1;
buffer.append((char)ch);
}
in.close();
return buffer.toString();
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
编码转化的过程如下:
       ByteToCharGB2312         CharToByteBig5
GB2312------------------>Unicode------------->Big5
执行 java gb2big5 gb.txt big5.txt ,如果 gb.txt 的内容是“今天星期三”,则得
到的文件 big5.txt 中的字符能够正确显示;而如果 gb.txt 的内容是“情人节快乐”
,则得到的文件 big5.txt 中对应于“节”和“乐”的字符都是符号“?”(0x3F),
可见 sun.io.ByteToCharGB2312 和 sun.io.CharToByteBig5 这两个基本类并没有编好

正如上例一样, Java 的基本类也可能存在问题。由于国际化的工作并不是在国内完成
的,所以在这些基本类发布之前,没有经过严格的测试,所以对中文字符的支持并不像
 Java Soft 所声称的那样完美。前不久,我的一位技术上的朋友发信给我说,他终于找
到了 Java Servlet 中文问题的根源。两周以来,他一直为 Java Servlet 的中文问题
所困扰,因为每面对一个含有中文字符的字符串都必须进行强制转换才能够得到正确的
结果(这好象是大家公认的唯一的解决办法)。后来,他确实不想如此继续安分下去了
,因为这样的事情确实不应该是高级程序员所要做的工作,他就找出 Servlet 解码的源
代码进行分析,因为他怀疑问题就出在解码这部分。经过四个小时的奋斗,他终于找到
了问题的根源所在。原来他的怀疑是正确的, Servlet 的解码部分完全没有考虑双字节
,直接把 %XX 当作一个字符。(原来 Java Soft 也会犯这幺低级的错误!)
如果你对这个问题有兴趣或者遇到了同样的烦恼的话,你可以按照他的步骤对 Servlet
.jar 进行修改:
找到源代码 HttpUtils 中的 static private String parseName ,在返回前将 sb(S
tringBuffer) 复制成 byte bs[] ,然后 return new String(bs,”GB2312”)。作上
述修改后就需要自己解码了:
HashTable form=HttpUtils .parseQueryString(request.getQueryString())或者
form=HttpUtils.parsePostData(……)
千万别忘了编译后放到 Servlet.jar 里面。
五、 关于 Java 中文问题的总结
Java 编程语言成长于网络世界,这就要求 Java 对多国字符有很好的支持。 Java 编程
语言适应了计算的网络化的需求,为它能够在网络世界迅速成长奠定了坚实的基础。 J
ava 的缔造者 (Java Soft) 已经考虑到 Java 编程语言对多国字符的支持,只是现在
的解决方案有很多缺陷在里面,需要我们付诸一些补偿性的措施。而世界标准化组织也
在努力把人类所有的文字统一在一种编码之中,其中一种方案是 ISO10646 ,它用四个
字节来表示一个字符。当然,在这种方案未被采用之前,还是希望 Java Soft 能够严格
地测试它的产品,为用户带来更多的方便。
附一个用于从数据库和网络中取出中文乱码的处理函数,入参是有问题的字符串,出参
是问题已经解决了的字符串。
        String parseChinese(String in)
        {
                String s = null;
                byte temp [];
                if (in == null)
                {
                        System.out.println("Warn:Chinese null founded!");
                                return new String("");
                }
                try
                {
                        temp=in.getBytes("iso-8859-1");
                        temp=in.getBytes("iso-8859-1");
                        s = new String(temp);
                }
                {
                        System.out.println("Warn:Chinese null founded!");
                                return new String("");
                }
                try
                {
                        temp=in.getBytes("iso-8859-1");
                        s = new String(temp);
                }
                catch(UnsupportedEncodingException e)
                {
                        System.out.println (e.toString());
                }
                return s;
        }

作者简介
段明辉,清华大学电子工程系学生
现在正在清华大学微电子学研究所从事 Java 智能卡微处理器的研究和开发yaya_316 www68207295领导

BBS 水木清华站的 Java 讨论组,为众多 Java 技术应用者提供解决方案

//

/


 JAVA汉字显示问题的解决方案
     在基于 Java 语言的编程中,我们经常碰到汉字的处理及显示的问题。一大堆看不懂的乱码肯定

不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Java 语言默认的编码方式是

UNICODE ,而我们中国人通常使用的文件和数据库都是基于 GB2312 或者 BIG5 等方式编码的,怎样才

能够恰当地选择汉字编码方式并正确地处理汉字的编码呢?本文将从汉字编码的常识入手,结合 Java

编程实例,分析以上两个问题并提出解决它们的方案。

  现在 Java 编程语言已经广泛应用于互联网世界,早在 Sun 公司开发 Java 语言的时候,就已经

考虑到对非英文字符的支持了。Sun 公司公布的 Java 运行环境(JRE)本身就分英文版和国际版,但

只有国际版才支持非英文字符。不过在 Java 编程语言的应用中,对中文字符的支持并非如同 Java

Soft 的标准规范中所宣称的那样完美,因为中文字符集不只一个,而且不同的操作系统对中文字符的

支持也不尽相同,所以会有许多和汉字编码处理有关的问题在我们进行应用开发中困扰着我们。有很多

关于这些问题的解答,但都比较琐碎,并不能够满足大家迫切解决问题的愿望,关于 Java 中文问题的

系统研究并不多,本文从汉字编码常识出发,分析 Java 中文问题,希望对大家解决这个问题有所帮助

  一、汉字编码的常识

  我们知道,英文字符一般是以一个字节来表示的,最常用的编码方法是 ASCII 。但一个字节最多

只能区分256个字符,而汉字成千上万,所以现在都以双字节来表示汉字,为了能够与英文字符分开,

每个字节的最高位一定为1,这样双字节最多可以表示64K格字符。我们经常碰到的编码方式有 GB2312

、BIG5、UNICODE 等。关于具体编码方式的详细资料,有兴趣的读者可以查阅相关资料。我肤浅谈一下

和我们关系密切的 GB2312 和 UNICODE。GB2312 码,中华人民共和国国家标准汉字信息交换用编码,

是一个由中华人民共和国国家标准总局发布的关于简化汉字的编码,通行于中国大陆地区及新加坡,简

称国标码。两个字节中,第一个字节(高字节)的值为区号值加32(20H),第二个字节(低字节)的

值为位号值加32(20H),用这两个值来表示一个汉字的编码。UNICODE 码是微软提出的解决多国字符

问题的多字节等长编码,它对英文字符采取前面加“0”字节的策略实现等长兼容。如 “A” 的 ASCII

码为0x41,UNICODE 就为0x00,0x41。利用特殊的工具各种编码之间可以互相转换。

  二、Java 中文问题的初步认识

  我们基于 Java 编程语言进行应用开发时,不可避免地要处理中文。Java 编程语言默认的编码方

式是 UNICODE,而我们通常使用的数据库及文件都是基于 GB2312 编码的,我们经常碰到这样的情况:

浏览基于 JSP 技术的网站看到的是乱码,文件打开后看到的也是乱码,被 Java 修改过的数据库的内

容在别的场合应用时无法继续正确地提供信息。

  String sEnglish = “apple”;

  String sChinese = “苹果”;

  String s = “苹果 apple ”;

  sEnglish 的长度是5,sChinese的长度是4,而 s 默认的长度是14。对于 sEnglish来说, Java

中的各个类都支持得非常好,肯定能够正确显示。但对于 sChinese 和 s 来说,虽然 Java Soft 声明

Java 的基本类已经考虑到对多国字符的支持(默认 UNICODE 编码),但是如果操作系统的默认编码不

是 UNICODE ,而是国标码等。从 Java 源代码到得到正确的结果,要经过 “Java 源代码-> Java 字

节码-> ;虚拟机->操作系统->显示设备”的过程。在上述过程中的每一步骤,我们都必须正确地处理汉

字的编码,才能够使最终的显示结果正确。

  “ Java 源代码-> Java 字节码”,标准的 Java 编译器 javac 使用的字符集是系统默认的字符

集,比如在中文 Windows 操作系统上就是 GBK ,而在 Linux 操作系统上就是ISO-8859-1,所以大家会

发现在 Linux 操作系统上编译的类中源文件中的中文字符都出了问题,解决的办法就是在编译的时候

添加 encoding 参数,这样才能够与平台无关。用法是

  javac –encoding GBK。

  “ Java 字节码->虚拟机->操作系统”, Java 运行环境 (JRE) 分英文版和国际版,但只有国

际版才支持非英文字符。 Java 开发工具包 (JDK) 肯定支持多国字符,但并非所有的计算机用户都

安装了 JDK 。很多操作系统及应用软件为了能够更好的支持 Java ,都内嵌了 JRE 的国际版本,为自

己支持多国字符提供了方便。

  “操作系统->显示设备”,对于汉字来说,操作系统必须支持并能够显示它。英文操作系统如果不

搭配特殊的应用软件的话,是肯定不能够显示中文的。

  还有一个问题,就是在 Java 编程过程中,对中文字符进行正确的编码转换。例如,向网页输出中

文字符串的时候,不论你是用 out.println(string); // string 是含中文的字符串 还是用 ,都必须

作 UNICODE 到 GBK 的转换,或者手动,或者自动。在 JSP 1.0中,可以定义输出字符集,从而实现内

码的自动转换。
用法是 

  但是在一些 JSP 版本中并没有提供对输出字符集的支持,(例如 JSP 0.92),这就需要手动编码

输出了,方法非常多。最常用的方法是

  String s1 = request.getParameter(“keyword”);

  String s2 = new String(s1.getBytes(“ISO-8859-1”),”GBK”);

  getBytes 方法用于将中文字符以“ISO-8859-1”编码方式转化成字节数组,而“GBK” 是目标编

码方式。我们从以ISO-8859-1方式编码的数据库中读出中文字符串 s1 ,经过上述转换过程,在支持

GBK 字符集的操作系统和应用软件中就能够正确显示中文字符串 s2 。

  三、Java 中文问题的表层分析及处理

  背景

  开发环境
  JDK1.15
  Vcafe2.0
  JPadPro

  服务器端
  NT IIS
  Sybase System
  Jconnect(JDBC)

  客户端
  IE5.0
  Pwin98

  .CLASS 文件存放在服务器端,由客户端的浏览器运行 APPLET , APPLET 只起调入 FRAME 类等主

程序的作用。界面包括 Textfield ,TextArea,List,Choice 等。

  I. 取中文

  用 JDBC 执行 SELECT 语句从服务器端读取数据(中文)后,将数据用 APPEND 方法加到

TextArea(TA) ,不能正确显示。但加到 List 中时,大部分汉字却可正确显示。

  将数据按“ISO-8859-1” 编码方式转化为字节数组,再按系统缺省编码方式 (Default

Character Encoding) 转化为 STRING ,即可在 TA 和 List 中正确显示。

  程序段如下:

  dbstr2 = results.getString(1);

  //After reading the result from DB server,converting it to string.

  dbbyte1 = dbstr2.getBytes(“iso-8859-1”);

  dbstr1 = new String(dbbyte1);

  在转换字符串时不采用系统默认编码方式,而直接采用“ GBK” 或者 “GB2312” ,在 A 和 B 两

种情况下,从数据库取数据都没有问题。

  II. 写中文到数据库

  处理方式与“取中文”相逆,先将 SQL 语句按系统缺省编码方式转化为字节数组,再按“ISO-

8859-1”编码方式转化为 STRING ,最后送去执行,则中文信息可正确写入数据库。

  程序段如下:

  sqlstmt = tf_input.getText();

  //Before sending statement to DB server,converting it to sql statement.

  dbbyte1 = sqlstmt.getBytes();

  sqlstmt = newString(dbbyte1,”iso-8859-1”);

  _stmt = _con.createStatement();

  _stmt.executeUpdate(sqlstmt);

  ……

  问题:如果客户机上存在 CLASSPATH 指向 JDK 的 CLASSES.ZIP 时(称为 A 情况),上述程序代

码可正确执行。但是如果客户机只有浏览器,而没有 JDK 和 CLASSPATH 时(称为 B 情况),则汉字

无法正确转换。

  我们的分析:

  1.经过测试,在 A 情况下,程序运行时系统的缺省编码方式为 GBK 或者 GB2312 。在 B 情况下

,程序启动时浏览器的 JAVA 控制台中出现如下错误信息:

  Can't find resource for sun.awt.windows.awtLocalization_zh_CN

  然后系统的缺省编码方式为“8859-1”。

  2.如果在转换字符串时不采用系统缺省编码方式,而是直接采用 “GBK” 或“GB2312”,则在 A

情况下程序仍然可正常运行,在 B 情况下,系统出现错误:

  UnsupportedEncodingException。

  3.在客户机上,把 JDK 的 CLASSES.ZIP 解压后,放在另一个目录中, CLASSPATH 只包含该目录

。然后一边逐步删除该目录中的 .CLASS 文件,另一边运行测试程序,最后发现在一千多个 CLASS 文

件中,只有一个是必不可少的,该文件是: sun.io.CharToByteDoubleByte.class。

  将该文件拷到服务器端和其它的类放在一起,并在程序的开头 IMPORT 它,在 B 情况下程序仍然

无法正常运行。

  4.在 A 情况下,如果在 CLASSPTH 中去掉 sun.io.CharToByteDoubleByte.class ,则程序运行时

测得默认编码方式为“8859-1”,否则为 “GBK” 或 “GB2312” 。

  如果 JDK 的版本为1.2以上的话,在 B 情况下遇到的问题得到了很好的解决,测试的步骤同上,

有兴趣的读者可以尝试一下。

  四、Java 中文问题的根源分析及解决

  在简体中文 MS Windows 98 + JDK 1.3 下,可以用 System.getProperties() 得到 Java 运行环

境的一些基本属性,类 PoorChinese 可以帮助我们得到这些属性。

  类 PoorChinese 的源代码:

  public class PoorChinese {

  public static void main(String[] args) {

  System.getProperties().list(System.out);

  }

  }

  执行 java PoorChinese 后,我们会得到:

  系统变量 file.encoding 的值为 GBK ,user.language 的值为 zh , user.region 的值为 CN

,这些系统变量的值决定了系统默认的编码方式是 GBK 。

  在上述系统中,下面的代码将 GB2312 文件转换成 Big5 文件,它们能够帮助我们理解 Java 中汉

字编码的转化:

  import java.io.*;

  import java.util.*;

  public class gb2big5 {

  static int iCharNum=0;

  public static void main(String[] args) {

  System.out.println("Input GB2312 file, output Big5 file.");

  if (args.length!=2) {

  System.err.println("Usage: jview gb2big5 gbfile big5file");

  System.exit(1);

  }

  String inputString = readInput(args[0]);

  writeOutput(inputString,args[1]);

  System.out.println("Number of Characters in file: "+iCharNum+".");

  }

  static void writeOutput(String str, String strOutFile) {

  try {

  FileOutputStream fos = new FileOutputStream(strOutFile);

  Writer out = new OutputStreamWriter(fos, "Big5");

  out.write(str);

  out.close();

  }

  catch (IOException e) {

  e.printStackTrace();

  e.printStackTrace();

  }

  }

  static String readInput(String strInFile) {

  StringBuffer buffer = new StringBuffer();

  try {

  FileInputStream fis = new FileInputStream(strInFile);

  InputStreamReader isr = new InputStreamReader(fis, "GB2312");

  Reader in = new BufferedReader(isr);

  int ch;

  while ((ch = in.read()) > -1) {

  iCharNum += 1;

  buffer.append((char)ch);

  }

  in.close();

  return buffer.toString();

  }

  catch (IOException e) {

  e.printStackTrace();

  return null;

  }

  }

  }


  编码转化的过程如下:

  ByteToCharGB2312 CharToByteBig5

  GB2312------------------>Unicode------------->Big5

  执行 java gb2big5 gb.txt big5.txt ,如果 gb.txt 的内容是“今天星期三”,则得到的文件

big5.txt 中的字符能够正确显示;而如果 gb.txt 的内容是“情人节快乐”,则得到的文件 big5.txt

中对应于“节”和“乐”的字符都是符号“?”(0x3F),可见 sun.io.ByteToCharGB2312 和

sun.io.CharToByteBig5 这两个基本类并没有编好。

  正如上例一样, Java 的基本类也可能存在问题。由于国际化的工作并不是在国内完成的,所以在

这些基本类发布之前,没有经过严格的测试,所以对中文字符的支持并不像 Java Soft 所声称的那样

完美。前不久,我的一位技术上的朋友发信给我说,他终于找到了 Java Servlet 中文问题的根源。两

周以来,他一直为 Java Servlet 的中文问题所困扰,因为每面对一个含有中文字符的字符串都必须进

行强制转换才能够得到正确的结果(这好象是大家公认的唯一的解决办法)。后来,他确实不想如此继

续安分下去了,因为这样的事情确实不应该是高级程序员所要做的工作,他就找出 Servlet 解码的源

代码进行分析,因为他怀疑问题就出在解码这部分。经过四个小时的奋斗,他终于找到了问题的根源所

在。原来他的怀疑是正确的, Servlet 的解码部分完全没有考虑双字节,直接把 %XX 当作一个字符。

(原来 Java Soft 也会犯这幺低级的错误!)

  如果你对这个问题有兴趣或者遇到了同样的烦恼的话,你可以按照他的步骤对 Servlet.jar 进行

修改:

  找到源代码 HttpUtils 中的 static private String parseName ,在返回前将 sb

(StringBuffer) 复制成 byte bs[] ,然后 return new String(bs,”GB2312”)。作上述修改后就

需要自己解码了:

  HashTable form=HttpUtils .parseQueryString(request.getQueryString())或者

form=HttpUtils.parsePostData(……)

  千万别忘了编译后放到 Servlet.jar 里面。

  五、 关于 Java 中文问题的总结

  Java 编程语言成长于网络世界,这就要求 Java 对多国字符有很好的支持。 Java 编程语言适应

了计算的网络化的需求,为它能够在网络世界迅速成长奠定了坚实的基础。 Java 的缔造者 (Java

Soft) 已经考虑到 Java 编程语言对多国字符的支持,只是现在的解决方案有很多缺陷在里面,需要

我们付诸一些补偿性的措施。而世界标准化组织也在努力把人类所有的文字统一在一种编码之中,其中

一种方案是 ISO10646 ,它用四个字节来表示一个字符。当然,在这种方案未被采用之前,还是希望

Java Soft 能够严格地测试它的产品,为用户带来更多的方便。

  附一个用于从数据库和网络中取出中文乱码的处理函数,入参是有问题的字符串,出参是问题已经

解决了的字符串。

  String parseChinese(String in)

  {

  String s = null;

  byte temp [];

  if (in == null)

  {

  System.out.println("Warn:Chinese null founded!");

  return new String("");

  }

  try

  {

  temp=in.getBytes("iso-8859-1");

  temp=in.getBytes("iso-8859-1");

  s = new String(temp);

  }

  {

  System.out.println("Warn:Chinese null founded!");

  return new String("");

  }

  try

  {

  temp=in.getBytes("iso-8859-1");

  s = new String(temp);

  }

  catch(UnsupportedEncodingException e)

  {

  System.out.println (e.toString());

  }

  return s;

  }


///
 深入剖析Java编程中的中文问题及建议最优解决方法--上篇
深入剖析Java编程中的中文问题及建议最优解决方法--上篇

 
说明:本文为作者原创,作者联系地址为:josserchai@yahoo.com。由于Java编程中的中文问题是一个

老生常谈的问题,在阅读了许多关于Java中文问题解决方法之后,结合作者的编程实践,我发现过去谈

的许多方法都不能清晰地说明问题及解决问题,尤其是跨平台时的中文问题。于是我给出此篇文章,内

容包括对控制台运行的class、Servelets、JSP及EJB类中的中文问题我剖析和建议解决办法。希望大家

指教。任何引用本文请注明出处!! 
Abstract:本文深入分析了Java程序设计中Java编译器对java源文件和JVM对class类文件的编码/解码

过程,通过此过程的解析透视出了Java编程中中文问题产生的根本原因,最后给出了建议的最优化的解

决Java中文问题的方法。 
1、中文问题的来源
    计算机最初的操作系统支持的编码是单字节的字符编码,于是,在计算机中一切处理程序最初都是

以单字节编码的英文为准进行处理。随着计算机的发展,为了适应世界其它民族的语言(当然包括我们

的汉字),人们提出了UNICODE编码,它采用双字节编码,兼容英文字符和其它民族的双字节字符编码

,所以,目前,大多数国际性的软件内部均采用UNICODE编码,在软件运行时,它获得本地支持系统(

多数时间是操作系统)默认支持的编码格式,然后再将软件内部的UNICODE转化为本地系统默认支持的

格式显示出来。Java的JDK和JVM即是如此,我这里说的JDK是指国际版的JDK,我们大多数程序员使用的

是国际化的JDK版本,以下所有的JDK均指国际化的JDK版本。我们的汉字是双字节编码语言,为了能让

计算机处理中文,我们自己制定的gb2312、GBK、GBK2K等标准以适应计算机处理的需求。所以,大部分

的操作系统为了适应我们处理中文的需求,均定制有中文操作系统,它们采用的是GBK,GB2312编码格式

以正确显示我们的汉字。如:中文Win2K默认采用的是GBK编码显示,在中文WIN2k中保存文件时默认采

用的保存文件的编码格式也是GBK的,即,所有在中文WIN2K中保存的文件它的内部编码默认均采用GBK

编码,注意:GBK是在GB2312基础上扩充来的。
    由于Java语言内部采用UNICODE编码,所以在JAVA程序运行时,就存在着一个从UNICODE编码和对应

的操作系统及浏览器支持的编码格式转换输入、输出的问题,这个转换过程有着一系列的步骤,如果其

中任何一步出错,则显示出来的汉字就会出是乱码,这就是我们常见的JAVA中文问题。
    同时,Java是一个跨平台的编程语言,也即我们编写的程序不仅能在中文windows上运行,也能在

中文Linux等系统上运行,同时也要求能在英文等系统上运行(我们经常看到有人把在中文win2k上编写

的JAVA程序,移植到英文Linux上运行)。这种移植操作也会带来中文问题。
    还有,有人使用英文的操作系统和英文的IE等浏览器,来运行带中文字符的程序和浏览中文网页,

它们本身就不支持中文,也会带来中文问题。
    有,几乎所有的浏览器默认在传递参数时都是以UTF-8编码格式来传递,而不是按中文编码传递,

所以,传递中文参数时也会有问题,从而带来乱码现象。
    总之,以上几个方面是JAVA中的中文问题的主要来源,我们把以上原因造成的程序不能正确运行而

产生的问题称作:JAVA中文问题。
2、JAVA编码转换的详细过程
    我们常见的JAVA程序包括以下类别:
     *直接在console上运行的类(包括可视化界面的类)
     *JSP代码类(注:JSP是Servlets类的变型)
     *Servelets类
     *EJB类
     *其它不可以直接运行的支持类
    这些类文件中,都有可能含有中文字符串,并且我们常用前三类JAVA程序和用户直接交互,用于输

出和输入字符,如:我们在JSP和Servlet中得到客户端送来的字符,这些字符也包括中文字符。无论这

些JAVA类的作用如何,这些JAVA程序的生命周期都是这样的:
    *编程人员在一定的操作系统上选择一个合适的编辑软件来实现源程序代码并以.java扩展名保存在

操作系统中,例如我们在中文win2k中用记事本编辑一个java源程序;
     *编程人员用JDK中的javac.exe来编译这些源代码,形成.class类(JSP文件是由容器调用JDK来编

译的);
     *直接运行这些类或将这些类布署到WEB容器中去运行,并输出结果。
    那么,在这些过程中,JDK和JVM是如何将这些文件如何编码和解码并运行的呢?
    这里,我们以中文win2k操作系统为例说明JAVA类是如何来编码和被解码的。
    第一步,我们在中文win2k中用编辑软件如记事本编写一个Java源程序文件(包括以上五类JAVA程序

),程序文件在保存时默认采用了操作系统默认支持GBK编码格式(操作系统默认支持的格式为

file.encoding格式)形成了一个.java文件,也即,java程序在被编译前,我们的JAVA源程序文件是采

用操作系统默认支持的file.encoding编码格式保存的,java源程序中含有中文信息字符和英文程序代

码;要查看系统的file.encoding参数,可以用以下代码:
public class ShowSystemDefaultEncoding {
public static void main(String[] args) {
String encoding = System.getProperty("file.encoding");
System.out.println(encoding);
}}
    第二步,我们用JDK的javac.exe文件编译我们的Java源程序,由于JDK是国际版的,在编译的时候

,如果我们没有用-encoding参数指定我们的JAVA源程序的编码格式,则javac.exe首先获得我们操作系

统默认采用的编码格式,也即在编译java程序时,若我们不指定源程序文件的编码格式,JDK首先获得

操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式,如WIN2k,它的值为GBK),然

后JDK就把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的UNICODE格式放入内存中

。然后,javac把转换后的unicode格式的文件进行编译成.class类文件,此时.class文件是UNICODE编

码的,它暂放在内存中,紧接着,JDK将此以UNICODE编码的编译后的class文件保存到我们的操作系统

中形成我们见到的.class文件。对我们来说,我们最终获得的.class文件是内容以UNICODE编码格式保

存的类文件,它内部包含我们源程序中的中文字符串,只不过此时它己经由file.encoding格式转化为

UNICODE格式了。
    这一步中,对于JSP源程序文件是不同的,对于JSP,这个过程是这样的:即WEB容器调用JSP编译器

,JSP编译器先查看JSP文件中是否设置有文件编码格式,如果JSP文件中没有设置JSP文件的编码格式,

则JSP编译器调用JDK先把JSP文件用JVM默认的字符编码格式(也即WEB容器所在的操作系统的默认的

file.encoding)转化为临时的Servlet类,然后再把它编译成UNICODE格式的class类,并保存在临时文

件夹中。如:在中文win2k上,WEB容器就把JSP文件从GBK编码格式转化为UNICODE格式,然后编译成临

时保存的Servlet类,以响应用户的请求。
    第三步,运行第二步编译出来的类,分为三种情况:
    A、 直接在console上运行的类
    B、 EJB类和不可以直接运行的支持类(如JavaBean类)
    C、 JSP代码和Servlet类
    D、 JAVA程序和数据库之间
    下面我们分这四种情况来看。
    A、直接在console上运行的类
    这种情况,运行该类首先需要JVM支持,即操作系统中必须安装有JRE。运行过程是这样的:首先

java启动JVM,此时JVM读出操作系统中保存的class文件并把内容读入内存中,此时内存中为UNICODE格

式的class类,然后JVM运行它,如果此时此类需要接收用户输入,则类会默认用file.encoding编码格

式对用户输入的串进行编码并转化为unicode保存入内存(用户可以设置输入流的编码格式)。程序运

行后,产生的字符串(UNICODE编码的)再回交给JVM,最后JRE把此字符串再转化为file.encoding格式

(用户可以设置输出流的编码格式)传递给操作系统显示接口并输出到界面上。
    对于这种直接在console上运行的类,它的转化过程可用图1更加明确的表示出来:

 

图1
    以上每一步的转化都需要正确的编码格式转化,才能最终不出现乱码现象。
    B、EJB类和不可以直接运行的支持类(如JavaBean类)
    由于EJB类和不可以直接运行的支持类,它们一般不与用户直接交互输入和输出,它们常常与其它

的类进行交互输入和输出,所以它们在第二步被编译后,就形成了内容是UNICODE编码的类保存在操作

系统中了,以后只要它与其它的类之间的交互在参数传递过程中没有丢失,则它就会正确的运行。
这种EJB类和不可以直接运行的支持类, 它的转化过程可用图2更加明确的表示出来:

 

图2
    C、JSP代码和Servlet类
    经过第二步后,JSP文件也被转化为Servlets类文件,只不过它不像标准的Servlets一校存在于

classes目录中,它存在于WEB容器的临时目录中,故这一步中我们也把它做为Servlets来看。
    对于Servlets,客户端请求它时,WEB容器调用它的JVM来运行Servlet,首先,JVM把Servlet的

class类从系统中读出并装入内存中,内存中是以UNICODE编码的Servlet类的代码,然后JVM在内存中运

行该Servlet类,如果Servlet在运行的过程中,需要接受从客户端传来的字符如:表单输入的值和URL

中传入的值,此时如果程序中没有设定接受参数时采用的编码格式,则WEB容器会默认采用ISO-8859-1

编码格式来接受传入的值并在JVM中转化为UNICODE格式的保存在WEB容器的内存中。Servlet运行后生成

输出,输出的字符串是UNICODE格式的,紧接着,容器将Servlet运行产生的UNICODE格式的串(如html

语法,用户输出的串等)直接发送到客户端浏览器上并输出给用户,如果此时指定了发送时输出的编码

格式,则按指定的编码格式输出到浏览器上,如果没有指定,则默认按ISO-8859-1编码发送到客户的浏

览器上。这种JSP代码和Servlet类,它的转化过程可用图3更加明确地表示出来:

 

图3
    D、Java程序和数据库之间
    对于几乎所有数据库的JDBC驱动程序,默认的在JAVA程序和数据库之间传递数据都是以ISO-8859-1

为默认编码格式的,所以,我们的程序在向数据库内存储包含中文的数据时,JDBC首先是把程序内部的

UNICODE编码格式的数据转化为ISO-8859-1的格式,然后传递到数据库中,在数据库保存数据时,它默

认即以ISO-8859-1保存,所以,这是为什么我们常常在数据库中读出的中文数据是乱码。
    对于JAVA程序和数据库之间的数据传递,我们可以用图4清晰地表示出来

 

图4
    3、分析常见的JAVA中文问题几个必须清楚的原则
    首先,经过上面的详细分析,我们可以清晰地看到,任何JAVA程序的生命期中,其编码转换的关键

过程是在于:最初编译成class文件的转码和最终向用户输出的转码过程。
    其次,我们必须了解JAVA在编译时支持的、常用的编码格式有以下几种:
    *ISO-8859-1,8-bit, 同8859_1,ISO-8859-1,ISO_8859_1等编码
    *Cp1252,美国英语编码,同ANSI标准编码
    *UTF-8,同unicode编码
    *GB2312,同gb2312-80,gb2312-1980等编码
    *GBK , 同MS936,它是gb2312的扩充
    及其它的编码,如韩文、日文、繁体中文等。同时,我们要注意这些编码间的兼容关体系如下:
    unicode和UTF-8编码是一一对应的关系。GB2312可以认为是GBK的子集,即GBK编码是在gb2312上扩

展来的。同时,GBK编码包含了20902个汉字,编码范围为:0x8140-0xfefe,所有的字符可以一一对应

到UNICODE2.0中来。
    再次,对于放在操作系统中的.java源程序文件,在编译时,我们可以指定它内容的编码格式,具

体来说用-encoding来指定。注意:如果源程序中含有中文字符,而你用-encoding指定为其它的编码字

符,显然是要出错的。用-encoding指定源文件的编码方式为GBK或gb2312,无论我们在什么系统上编译

含有中文字符的JAVA源程序都不会有问题,它都会正确地将中文转化为UNICODE存储在class文件中。
   
然后,我们必须清楚,几乎所有的WEB容器在其内部默认的字符编码格式都是以ISO-8859-1为默认值的

,同时,几乎所有的浏览器在传递参数时都是默认以UTF-8的方式来传递参数的。所以,虽然我们的

Java源文件在出入口的地方指定了正确的编码方式,但其在容器内部运行时还是以ISO-8859-1来处理的


 


 深入剖析Java编程中的中文问题及建议最优解决方法---下篇
深入剖析Java编程中的中文问题及建议最优解决方法

 
说明:本文为作者原创,作者联系地址为:josserchai@yahoo.com。由于Java编程中的中文问题是一个

老生常谈的问题,在阅读了许多关于Java中文问题解决方法之后,结合作者的编程实践,我发现过去谈

的许多方法都不能清晰地说明问题及解决问题,尤其是跨平台时的中文问题。于是我给出此篇文章,内

容包括对控制台运行的class、Servelets、JSP及EJB类中的中文问题我剖析和建议解决办法。希望大家

指教。任何引用本文请注明出处!! 
    4、中文问题的分类及其建议最优解决办法
    了解以上JAVA处理文件的原理之后,我们就可以提出了一套建议最优的解决汉字问题的办法。
    我们的目标是:我们在中文系统中编辑的含有中文字符串或进行中文处理的JAVA源程序经编译后可

以移值到任何其它的操作系统中正确运行,或拿到其它操作系统中编译后能正确运行,能正确地传递中

文和英文参数,能正确地和数据库交流中英文字符串。
    我们的具体思路是:在JAVA程序转码的入口和出口及JAVA程序同用户有输入输出转换的地方限制编

码方法使之正确即可。
    具体解决办法如下:
    1、 针对直接在console上运行的类
    对于这种情况,我们建议在程序编写时,如果需要从用户端接收用户的可能含有中文的输入或含有

中文的输出,程序中应该采用字符流来处理输入和输出,具体来说,应用以下面向字符型节点流类型:
    对文件:FileReader,FileWrieter
        其字节型节点流类型为:FileInputStream,FileOutputStream
    对内存(数组):CharArrayReader,CharArrayWriter
        其字节型节点流类型为:ByteArrayInputStream,ByteArrayOutputStream
    对内存(字符串):StringReader,StringWriter
    对管道:PipedReader,PipedWriter
        其字节型节点流类型为:PipedInputStream,PipedOutputStream
    同时,应该用以下面向字符型处理流来处理输入和输出:
    BufferedWriter,BufferedReader
        其字节型的处理流为:BufferedInputeStream,BufferedOutputStream
    InputStreamReader,OutputStreamWriter
    其字节型的处理流为:DataInputStream,DataOutputStream
    其中InputStreamReader和InputStreamWriter用于将字节流按照指定的字符编码集转换到字符流,

如:
    InputStreamReader in = new InputStreamReader(System.in,"GB2312");
    OutputStreamWriter out = new OutputStreamWriter (System.out,"GB2312");
    例如:采用如下的示例JAVA编码就达到了要求:
//Read.java
import java.io.*;
public class Read {
public static void main(String[] args) throws IOException {
String str = "/n中文测试,这是内部硬编码的串"+"/ntest english character";
String strin= "";
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in,"gb2312")); //设

置输入接口按中文编码
BufferedWriter stdout = new BufferedWriter(new OutputStreamWriter(System.out,"gb2312"));

//设置输出接口按中文编码
stdout.write("请输入:");
stdout.flush();
strin = stdin.readLine();
stdout.write("这是从用户输入的串:"+strin);
stdout.write(str);
stdout.flush();
}}
    同时,在编译程序时,我们用以下方式来进行:
    javac -encoding gb2312 Read.java
    其运行结果如图5所示:

    图5
    2、 针对EJB类和不可以直接运行的支持类(如JavaBean类)
    由于这种类它们本身被其它的类调用,不直接与用户交互,故对这种类来说,我们的建议的处理方

式是内部程序中应该采用字符流来处理程序内部的中文字符串(具体如上面一节中一样),同时,在编

译类时用-encoding gb2312参数指示源文件是中文格式编码的即可。
    3、 针对Servlet类
    针对Servlet,我们建议用以下方法:
    在编译Servlet类的源程序时,用-encoding指定编码为GBK或GB2312,且在向用户输出时的编码部

分用response对象的setContentType("text/html;charset=GBK");或gb2312来设置输出编码格式,同样

在接收用户输入时,我们用request.setCharacterEncoding("GB2312");这样无论我们的servlet类移

植到什么操作系统中,只有客户端的浏览器支持中文显示,就可以正确显示。如下是一个正确的示例:
//HelloWorld.java
package hello;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet
{
public void init() throws ServletException { }
public void doGet(HttpServletRequest request, HttpServletResponse response) throws

IOException, ServletException
{
request.setCharacterEncoding("GB2312"); //设置输入编码格式
response.setContentType("text/html;charset=GB2312"); //设置输出编码格式
PrintWriter out = response.getWriter(); //建议使用PrintWriter输出
out.println("<hr>");
out.println("Hello World! This is created by Servlet!测试中文!");
out.println("<hr>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws

IOException, ServletException
{
request.setCharacterEncoding("GB2312"); //设置输入编码格式
response.setContentType("text/html;charset=GB2312"); //设置输出编码格式
String name = request.getParameter("name");
String id = request.getParameter("id");
if(name==null) name="";
if(id==null) id="";
PrintWriter out = response.getWriter(); //建议使用PrintWriter输出
out.println("<hr>");
out.println("你传入的中文字串是:" + name);
out.println("<hr>你输入的id是:" + id);
out.println("<hr>");
}
public void destroy() { }
}
    请用javac -encoding gb2312 HelloWorld.java来编译此程序。
    测试此Servlet的程序如下所示:
<%@page contentType="text/html; charset=gb2312"%>
<%request.setCharacterEncoding("GB2312");%>
<html><head><title></title>
<Script language="JavaScript">
function Submit() {
//通过URL传递中文字符串值给Servlet
document.base.action = "./HelloWorld?name=中文";
document.base.method = "POST";
document.base.submit();
}
</Script>
</head>
<body bgcolor="#FFFFFF" text="#000000" topmargin="5">
<form name="base" method = "POST" target="_self">
<input name="id" type="text" value="" size="30">
<a href = "JavaScript:Submit()">传给Servlet</a>
</form></body></html>
    其运行结果如图6所示:

    图6
    4、 JAVA程序和数据库之间
    为避免JAVA程序和数据库之间数据传递出现乱码现象,我们建议采用以下最优方法来处理:
    1、 对于JAVA程序的处理方法按我们指定的方法处理。
    2、 把数据库默认支持的编码格式改为GBK或GB2312的。
    如:在mysql中,我们可以在配置文件my.ini中加入以下语句实现:
    在[mysqld]区增加:
    default-character-set=gbk
    并增加:
    [client]
    default-character-set=gbk
    在SQL Server2K中,我们可以将数据库默认的语言设置为Simplified Chinese来达到目的。
    5、 针对JSP代码
    由于JSP是在运行时,由WEB容器进行动态编译的,如果我们没有指定JSP源文件的编码格式,则JSP

编译器会获得服务器操作系统的file.encoding值来对JSP文件编译的,它在移植时最容易出问题,如在

中文win2k中可以很好运行的jsp文件拿到英文linux中就不行,尽管客户端都是一样的,那是因为容器

在编译JSP文件时获取的操作系统的编码不同造成的(在中文wink中的file.encoding和在英文Linux中

file.encoding是不同的,且英文Linux的file.encoding对中文不支持,所以编译出来的JSP类就会有问

题)。网络上讨论的大多数是此类问题,多是因为JSP文件移植平台时不能正确显示的问题,对于这类

问题,我们了解了JAVA中程序编码转换的原理,解决起来就容易多了。我们建议的解决办法如下:
    1、我们要保证JSP向客户端输出时是采用中文编码方式输出的,即无论如何我们首先在我们的JSP

源代编中加入以下一行:
    <%@page contentType="text/html; charset=gb2312"%>
    2、为了让JSP能正确获得传入的参数,我们在JSP源文件头加入下面一句:
    <%request.setCharacterEncoding("GB2312");%>
    3、为了让JSP编译器能正确地解码我们的含有中文字符的JSP文件,我们需要在JSP源文件中指定我

们的JSP源文件的编码格式,具体来说,我们在JSP源文件头上加入下面的一句即可:
    <%@page pageEncoding="GB2312"%>或<%@page pageEncoding="GBK"%>
    这是JSP规范2.0新增加的指令。
    我们建议使用此方法来解JSP文件中的中文问题,下面的代码是一个正确做法的JSP文件的测试程序


//testchinese.jsp
<%@page pageEncoding="GB2312"%>
<%@page contentType="text/html; charset=gb2312"%>
<%request.setCharacterEncoding("GB2312");%>
<%
String action = request.getParameter("ACTION");
String name = "";
String str = "";
if(action!=null && action.equals("SENT"))
{
name = request.getParameter("name");
str = request.getParameter("str");
}
%>
<html>
<head>
<title></title>
<Script language="JavaScript">
function Submit()
{
document.base.action = "?ACTION=SENT&str=传入的中文";
document.base.method = "POST";
document.base.submit();
}
</Script>
</head>
<body bgcolor="#FFFFFF" text="#000000" topmargin="5">
<form name="base" method = "POST" target="_self">
<input type="text" name="name" value="" size="30">
<a href = "JavaScript:Submit()">提交</a>
</form>
<%
if(action!=null && action.equals("SENT"))
{
out.println("<br>你输入的字符为:"+name);
out.println("<br>你通过URL传入的字符为:"+str);
}
%>
</body>
</html>
    如图7是此程序运行的结果示意图:

    图7
    5、总结
    在上面的详细分析中,我们清晰地给出了JAVA在处理源程序过程中的详细转换过程,为我们正确解

决JAVA编程中的中文问题提供了基础。同时,我们给出了认为是最优的解决JAVA中文问题的办法。
    6、参考资料
    1、段明辉.Java 编程技术中汉字问题的分析及解决.
        http://www-900.ibm.com/developerWorks/cn/java/java_chinese/index.shtml
    2、 周竞涛.关于Java中文问题的几条分析原则
        http://www-900.ibm.com/developerWorks/cn/java/l-javachinese/index.shtml
    7、作者介绍
        作者:柴政(abnerchai),1980年9月3日出生,高级程序员,西南交通大学计算机与通信工程

学院计算机应用技术硕士研究生。研究方向为:网络安全,基于J2EE/EJB的中间件技术,项目管理/工

作流等。作者联系方法:josserchai@yahoo.com 电话:028-87601939
 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值