Java中关于字符编码的一些思考

来源:互联网 发布:周易起名大师软件 编辑:程序博客网 时间:2024/05/21 10:18

字符编码

首先必须了解一下计算机中字符编码的概念。众所周知,计算机只认识二进制的内容,而人阅读则依赖于字符的内容,于是才有了诸如ASCII、utf-8、gbk和unicode等字符编码的产生。每一种字符编码都有一张映射表,分别记录了二进制内容 和 字符 之间的映射关系。将字符内容转为二进制内容的过程称为编码,而反之称为解码。

每个字符编码都有明确支持字符的范围,比如ASCII和ISO-8859-1都不支持中文字符。


(ASCII部分映射表)

Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,并且规定所有字符固定长度为两个字节(byte)。

UTF-8的特点是对不同范围的字符使用不同长度的编码,如对字母和数字只占一个字节,而汉字占了2-4个字节的(不定长)。虽然据说UTF-8并不完全支持所有汉字,但大多数情况下它还是我们开发系统的首选。


Unicode二进制 和 UTF-8二进制 之间的转换关系)


Java中的字符编码

由于Java是跨平台的,因此使用了Unicode字符编码。注意java中如何使用unicode其实是要深入研究的,比如所有class文件均使用了unicode编码(java文件就不一定),所有字符在jvm的内存里也是使用了unicode编码。
既然Java中的字符都是以unicode编码的,那就可以解释一些常见的问题,如以下所示:
(1)char a =’中’;      char在java中占两个字节,当然可以用来存储汉字(汉字在unicode中就是占两个字节)
(2)String.length()    返回的长度是字符的数量,因为String的内容保存在char[],而一个汉字也算一个字符。


Java中的String与byte[]之间的转换

在java的IO流中一般都是以byte(字节)作为传输单位的,我们从流中读到的byte[](即二进制内容)与String可通过如下关系进行转换:
//String -> byte[]String str = "你好!bo";byte[] utf8_bytes = str.getBytes("UTF-8");byte[] other_bytes = str.getBytes(); //使用JDK默认的字符集编码//byte[] -> StringString str1 = new String(utf8_bytes,"UTF-8");String str2 = new String(other_bytes);   //使用JDK默认的字符集编码
注意对于JDK默认的字符集编码,若在JDK参数中没有明确指定,则window默认为GBK,linux默认为UTF-8

这里还有一个地方是值得思考的。String本质上是由char[]实现的,而char在JVM内存里表现为unicode编码。当我们在进行String与byte[]之间的转换时,其实就是Unicode编码与其他编码的之间的格式转换(具体是Unicode编码的二进制内容与其他编码的二进制内容之间的转换),我想JVM肯定是有对应的转换关系的,毕竟计算机只能认得二进制的内容。

过程如下(不一定正确):

当我们使用String.getBytes(String charsetName)方法时,其实是将String字符串表示的二进制内容(Unicode编码)转化为charsetName编码对应的二进制内容。

当我们使用new String(byte[] bytes, String charsetName) 方法时,也应该是将bytes里的二进制内容按charsetName编码与Unicode编码的映射关系转换为Unicode编码的二进制内容,这样JVM便自动将转换好的二进制内容(Unicode编码)组装为char[]进而构造成String


乱码问题产生的原因


编码和解码使用的字符编码集不一致

String str = "你好!bo";byte[] utf8_bytes = str.getBytes("UTF-8");String test = new String(utf8_bytes,"iso-8859-1"); //编码解码不一致导致乱码test = new String(test.getBytes("iso-8859-1"),"UTF-8"); //矫正回正确字符
以上例子就是通讯的两端经常会出现的问题,有时候没法改去改别人的代码,那只好自己将乱码矫正回来。
解决这类问题的思路是明确String转为byte[]所用的编码,才能通过new String(byte[] bytes, String charsetName)得到正确的字符。因此在大多数系统中最好使用统一的编码如UTF-8,千万不要使用getBytes()String(byte[] bytes)此类和平台相关的API(可能会导致在Window上没问题部署到Linux出现乱码)。

使用了错误的字符编码集进行编码

比如ISO-8859-1不支持中文,如果强行进行以下操作:
byte[] bytes = "你好".getBytes("iso-8859-1");
这时得到的bytes是没有办法还原回正确的字符的,因为在ios-8859-1映射表里根本找不到该字符对应的二进制内容。可以进行如下测试,得到的结果为 ??
String str = new String(bytes,"iso-8859-1")
注意这种错误是不可恢复的,因为得到的bytes没有保存完整的内容,可以说这个过程是有损且不可逆的。


参考:
Java字符编码根本原理 http://lavasoft.blog.51cto.com/62575/273608/
字符集和字符编码 http://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html#_4.Accept-Charset/Accept-Encoding/Ac
java中文乱码解决之道 http://easygeek.com.cn/article/AzQjMv.html


0 0