java中的字符编码处理

  做java已经两年,一直在规范和框架中挣扎,往往很少去考虑一些细节上的原理.以为很多在规范中屏蔽了.今天由于一个用户的需求引发了一个中文处理的问题.问题是这样的:用户要在一个发布一篇即有简体也有繁体的文章.由于当前系统采用的GBK的编码,发现有些问题,开始马上想到换成unicode编码,还是乱码.晕......,下定决心把这个东东搞清楚...........

  上google,查出一大堆这类问题,有点描述太复杂,有的描述太过于个例化.最后在csdn上找到了AbnerChai 深入Java中文问题及最优解决方法的文章,非常详细,但是语言上有点罗唆.
其实说白了就是 显示--os--java处理(java程序,web容器,java组件)--jvm(jdk)--os--现实设备.对于核心的jvm来说,把载入的文件class一定是unicode的(当然我说的jvm应该是国际版).剩下的问题就是一个输入输出的问题了.还有就是在jvm前后进行编码转换时的编码源和编码目标怎么确定问题.
首先了解一下目前国际化编码:
1.ISO-8859 对于ISO-8859系列的字符集都想象成一个:2^8 = 16 * 16 = 256个格子的棋盘,这样所有的西文字符(英文)用这样一个16×16的坐标系就基本可以覆盖全了。而英文实际上只用其中小于128(/x80)的部分就够了。利用大于128部分的空间的不同定义规则形成了真对其他欧洲语言的扩展字符集:ISO-8859-2 ISO-8859-4等……

ISO-8859-1
ISO-8859-7
其他语言

英文 其他西欧字符
ōē
2.GB2312 BIG5 SJIS等多字节字符集(MultiByte Charsets):
对于亚洲语言来说:汉字这么多,用这么一个256格的小棋盘肯定放不下,所以要区别成千上万的汉字解决办法就是用2个字节(坐标)来定位一个“字”在棋盘上的位置,将以上规则做一个扩展:

如果第1个字符是小于128(/x80)的仍和英文字符集编码方式保持兼容;
如果第1个字符是大于128(/x80)的,就当成是汉字的第1个字节,这个自己和后面紧跟的1个字节组成一个汉字;
其结果相当于在位于128以上的小棋格里每个小棋格又划分出了一个16×16的小棋盘。这样一个棋盘中的格子数(可能容纳的字符数)就变成了128 + 128 * 256。按照类似的方式有了简体中文的GB2312标准,繁体中文的BIG5字符集和日文的SJIS字符集等,GB2312字符集包含大约有六仟多个常用简体汉字。
………………………………

由此可以看出,所有这些从ASCII扩展式的编码方式中:英文部分都是兼容的,但扩展部分的编码方式是不兼容的,虽然很多字在3种体系中写法一致(比如“中文”这2个字)但在相应字符集中的坐标不一致,所以GB2312编写的页面用BIG5看就变得面目全非了。而且有时候经常在浏览其他非英语国家的页面时(比如包含有德语的人名时)经常出现奇怪的汉字,其实就是扩展位的编码冲突造成的。

3.GBK和GB18030理解成一个小UNICODE:GBK字符集是GB2312的扩展(K),GBK里大约有贰万玖仟多个字符,除了保持和GB2312兼容外,繁体中文字,甚至连日文的假名字符也能显示。而GB18030-2000则是一个更复杂的字符集,采用变长字节的编码方式,能够支持更多的字符。关于汉字的编码方式比较

详细的定义规范可以参考:

http://www.unihan.com.cn/cjk/ana17.htm


ASCII(英文) ==> 西欧文字 ==> 东欧字符集(俄文,希腊语等) ==> 东亚字符集(GB2312 BIG5 SJIS等)==> 扩展字符集GBK GB18030这个发展过程基本上也反映了字符集标准的发展过程,但这么随着时间的推移,尤其是互联网让跨语言的信息的交互变得越来越多的时候,太多多针对本地语言的编码标准的出现导致一个应用程序的国际化变得成本非常高。尤其是你要编写一个同时包含法文和简体中文的文档,这时候一般都会想到要是用一个通用的字符集能够显示所有语言的所有文字就好了,而且这样做应用也能够比较方便的国际化,为了达到这个目标,即使应用牺牲一些空间和程序效率也是非常值得的。UNICODE就是这样一个通用的解决方案。

4.UNICODE双字节字符集:
所以你可以把UNICODE想象成这样:让所有的字符(包括英文)都用2个字节(2个8位)表示,这样就有了一个2^(8*2) = 256 * 256 = 65536个格子的大棋盘。在这个棋盘中,这样中(简繁)日韩(还包括越南)文字作为CJK字符集都放在一定的区位内,为了减少重复,各种语言中写法一样的字共享一个“棋格”。

接下来就是怎么看java处理编码问题了.
我们知道jvm处理的字节码一定是unicode,对于没有产生jvm之外的输入和输出的话,任何处理都不会带来乱码问题.例如javaBean ,ejb等不会产生编码问题.
常用的输入:
用户和java程序交互(java组件解析或转换器编码)
jsp/servlet 等客户端的输入(通过web服务器获得传输的信息默认编码)
数据库 通过jdbc获得默认的信息编码
jsp/java等源文件到字节码(unicode)的输入(有jsp编译器和javac来转换源文件的编码)
第一种情况:就是java程序运行在console上等待用的信息输入,这是java类会默认用file.encoding编码格式对用户输入的串进行编码并转化为unicode保存入内存(用户可以设置输入流的编码格式,这可以理解java组件做转换器)。
编码来源:默认os编码,或者用户在类中自己设定.编码目标:jvm处理编码unicode.这里编码源必须要能包含输入的字符集.
第二种情况:就是jvm装入servlet进行接受表单输入的值和URL中传入的值,此时如果程序中没有设定接受参数时采用的编码格式,则WEB容器会默认采用ISO-8859-1编码格式来接受传入的值并在JVM中转化为UNICODE格式的保存在WEB容器的内存中。
编码来源:web容器,或者用filter 进行request.setCharacterEncoding(),编码目标:jvm处理编码unicode.这里的编码源必须能包含输入的字符集.
第三种情况:数据库的JDBC驱动程序,默认的在Java程序和数据库之间传递数据都是以ISO-8859-1为默认编码格式的,所以,我们的程序在向数据库内读取数据时会把数据转化成unicode.
编码来源:默认jdbc编码 iso,jdbc设定的编码格式.编码目标:jvm处理的unicode.这里的源必须能包含数据存储的字符集.
第四种情况:java通过平台编写,获得记事本编写,有个文件编码,一般默认系统编码,有的工具可以改.通过javac进行编译生成class文件unicode编码.jvm可以处理unicode的文件.jsp编译器编译jsp时也是一样,可以用jsp的pageEncoding 设置jsp文件的源编码.
编码来源:有javac获得系统默认或者-encoding参数来确定编码源.编码目标:jvm处理的class文件.jsp只是多了一个编译成java的过程.原理一样.这里的编码源设置必须保证能覆盖文件种的字符集.

综合上面的输入情况,实际细心的读者会发现,这里有两种情况,一种是文件,一种是字节流,文件的话编码可以设置在文件头,例如jsp,xml等,字节流必须在java处理之前指定能覆盖其字符集的编码.(注意一定是字节流,字符流是已经进行了编码的).只要保证java处理的编码源正确一定可以能让java正确处理.

输出的情况相对简单的多.jvm出来的信息是unicode,你要怎么处理只要能让os的编码集认识,现实一定不会有问题.
例如.console上java会以file.encoding格式传会给操作系统
jsp客户设置charSet:默认按ISO-8859-1编码
jdbc可以设置其输入编码:默认按ISO-8859-1编码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值