java下的字符编码转换总结

时间:2022-01-11 08:40:11

       在java下进行软件开发的过程中,难免会遇到乱码的问题。如果不了解java字符编码转换的相关知识,很有可能就会对乱码一头雾水,搞不清道不明,活活恶心死!!

其实,只要认真搞清java下的编码知识,避免出现乱码、解决乱码问题都很简单!

一、各种编码的基本知识

1. iso8859-1

       属于单字节编码,最多能表示的字符范围是0-255,应用于英文系列。比如,字母'a'的编码为0x61=97。

很明显,iso8859-1编码表示的字符范围很窄,无法表示中文字符。但是,由于是单字节编码,和计算机最基础的表示单位一致,所以很多时候,仍旧使用iso8859-1编码来表示。而且在很多协议上,默认使用该编码。比如,虽然"中文"两个字不存在iso8859-1编码,以gb2312编码为例,应该是"d6d0 cec4"两个字符,使用iso8859-1编码的时候则将它拆开为4个字节来表示:"d6 d0 ce c4"(事实上,在进行存储的时候,也是以字节为单位处理的)。而如果是UTF编码,则是6个字节"e4 b8 ad e6 96 87"。很明显,这种表示方法还需要以另一种编码为基础。

2. GB2312/GBK

       这就是汉子的国标码,专门用来表示汉字,是双字节编码,而英文字母和iso8859-1一致(兼容iso8859-1编码)。其中gbk编码能够用来同时表示繁体字和简体字,而gb2312只能表示简体字,gbk是兼容gb2312编码的。

3. unicode

      这是最统一的编码,可以用来表示所有语言的字符,而且是定长双字节(也有四字节的)编码,包括英文字母在内。所以可以说它是不兼容iso8859-1编码的,也不兼容任何编码。不过,相对于iso8859-1编码来说,uniocode编码只是在前面增加了一个0字节,比如字母'a'为"00 61"。

     需要说明的是,定长编码便于计算机处理(注意GB2312/GBK不是定长编码),而unicode又可以用来表示所有字符,所以在很多软件内部是使用unicode编码来处理的,比如java。

4. UTF

      考虑到unicode编码不兼容iso8859-1编码,而且容易占用更多的空间:因为对于英文字母,unicode也需要两个字节来表示。所以unicode不便于传输和存储。因此而产生了utf编码,utf编码兼容iso8859-1编码,同时也可以用来表示所有语言的字符,不过,utf编码是不定长编码,每一个字符的长度从1-6个字节不等。另外,utf编码自带简单的校验功能。一般来讲,英文字母都是用一个字节表示,而汉字使用三个字节。

      注意,虽然说utf是为了使用更少的空间而使用的,但那只是相对于unicode编码来说,如果已经知道是汉字,则使用GB2312/GBK无疑是最节省的。不过另一方面,值得说明的是,虽然utf编码对汉字使用3个字节,但即使对于汉字网页,utf编码也会比unicode编码节省,因为网页中包含了很多的英文字符。

以上摘自网络

 

二、java下的三种编码

1.java源文件的编码

       java源文件的编码可以多种多样,一般都是系统默认的编码,通常为"gbk"。也就是说,java源文件中的所有字符的编码方式都是GBK。比如:

在Java代码中定义一个字符串: String s="汉字";//GBK编码的 但是,不管源文件是何种编码,当它被编译成class文件时,会统一转换成unicode编码,class文件的存储采用utf8的编码方式。 然而JVM运行时采用utf16。也就是说,JVM内存中存放的所有字符串都是unicode编码的 2.JVM平台默认的编码     在中文window环境,JVM平台默认的编码基本是“GBK”的,可以通过如下代码查看: System.out.println(Charset.defaultCharset());  //输出结果为“GBK” 3.外部资源的编码 尽管JVM运行时程序里的字符串都是unicode编码的,但是程序需要和外界各种“源”(比如数据库、文件等)交互,读写需要的数据。而这些“源”的编码方式是各种各样的,原因很简单,存储优化。如果在读写的时候不指定编码方式,就会按照JVM平台默认的编码(这里就是GBK)去读写,一旦“源”的编码方式与之不吻合,乱码就将产生。因此,一般java程序对“源”读写的时候最好指定编码方式,这样不容易出问题。  

三、一些容易混淆的地方

1.java下中文字符串的编解码

解码:

尽管JVM运行时内存中所有的字符串都是unicode编码的,但是我们依然可以获取中文字符串的各种编码下的字节形式,调用

getBytes(String charsetName)

就可以实现;这里如果不设置参数,就将采用JVM平台默认的字符集(GBK)进行解码.

编码:

以下转自网络:

new String(input.getBytes("ISO-8859-1"), "GB18030")  

     上面这段代码代表什么?有人会说: “把input字符串从ISO-8859-1编码方式转换成GB18030编码方式”。如果这种说法正确,那么又如何解释我们刚提到的java字符串都采用unicode编码呢?
这种说法不仅是欠妥的,而且是大错特错的,让我们一一来分析,其实事实是这样的:我们本应该用GB18030的编码来读取数据并解码成字符串,但结果却采用了ISO-8859-1的编码,导致生成一个错误的字符串。要恢复,就要先把字符串恢复成原始字节数组,然后通过正确的编码GB18030再次解码成字符串(即把以GB18030编码的数据转成unicode的字符串)。注意,字符串永远都是unicode编码的。
但编码转换并不是负负得正那么简单,这里我们之所以可以正确地转换回来,是因为 ISO8859-1 是单字节编码,所以每个字节被按照原样 转换为 String ,也就是说,虽然这是一个错误的转换,但编码没有改变,所以我们仍然有机会把编码转换回来!