java 编码(一)

本文深入探讨文本文件的编码方式,包括GBK、Unicode、UnicodeBE、UTF-8等,以及Windows如何识别编码方式。同时,阐述了Java中字符处理函数getBytes和newString的用途,解释了UTF-8编码规则和Java中String对象的unicode编码特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、 判断一个文本文件的编码方式

按照给定的字符集存储文本文件时,在文件的最开头的三个字节中就有可能存储着编码信息,所以,基本的原理就是只要读出文件前三个字节,判定这些字节的值,就可以得知其编码的格式。

 

 

二、 问题

使用Windows记事本的“另存为”,可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件,Windows是怎样识别编码方式的呢?

 

我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节,分别是FF、FE(Unicode),FE、FF(Unicode big endian),EF、BB、BF(UTF-8)。但这些标记是基于什么标准呢?

 

李:

ansi编码, 默认只GBK: C0 EE

unicode little: FF FE 4E 67

unicode big: FE FF 67 4E

utf-8: EF BB BF E6 9D 8E

 

课件gbk、unicode都使用两个字节来表示一个汉字,而utf-8则使用了三个字节。

utf-8编码,含有了unicode的编码信息。按照unicode的编码规范,即使是一个英文字母,也需要用两个字节(第一个字节为0x00)来表示,这对于一篇纯英文的文章,其大小相当于使用ASCII编码的两倍,对磁盘是很大的浪费。所以提出了utf-8不定长的编码方式,对于中文,用三个字节来表示,而对于英文字母,则使用一字节即可表示。

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

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

 

 

三、  Little endian和Big endian

如果一个文本文件的头两个字节是FE FF,就表示该文件采用大尾方式;如果头两个字节是FF FE,就表示该文件采用小尾方式。

 

UTF-8是Unicode的实现方式之一

UTF-8的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

 

四、  java对字符的处理 

3.1. getBytes(charset) 

这是java字符串处理的一个标准函数,其作用是将字符串所表示的字符按照charset编码,并以字节方式表示。注意字符串在java内存中总是按unicode编码存储的。比如"中文",正常情况下(即没有错误的时候)存储为"4e2d 6587",如果charset为"gbk",则被编码为"d6d0 cec4",然后返回字节"d6 d0 ce c4"。如果charset为"utf8"则最后是"e4b8 ad e6 96 87"。如果是"iso8859-1",则由于无法编码,最后返回 "3f 3f"(两个问号)。 

3.2. new String(charset) 

这是java字符串处理的另一个标准函数,和上一个函数的作用相反,将字节数组按照charset编码进行组合识别,最后转换为unicode存储。参考上述getBytes的例子,"gbk" 和"utf8"都可以得出正确的结果"4e2d 6587",但iso8859-1最后变成了"003f 003f"(两个问号)。 

在Java 中,String的getBytes()方法就是对特定的字符串(unicode)按照给定的字符集进行编码(encode),new String()则可以按照某个字符集将字节流转换回unicode(decode)。Java里面的每一个String都是unicode编码。

 

Java 里面的encode和decode都是相对于unicode而言的,encode的意思是将char[]--> XXX Encoding byte[],decode就是由XXX Encoding byte[] --> char[]。平常,当我们说“将GBK编码转换为UTF-8编码”的时候,实际的意思就是:GBKEncoding byte[] --> UTF-8 Encoding byte[],这种转换只有在需要用byte[]传输数据的时候才有意义,否则便是毫无意义的。

首先要说明的一点是:Java中的String对象就是一个unicode编码的字符串。

 

但是,我们通常会听到有人说:“我们需要将String由ISO-8859-1转换为GBK编码”,这又是怎么回事呢?实际上,我们并不是要“将 一个由ISO-8859-1编码的String转换为GBK编码的String”,反复说明的是,JAVA中的String都是unicode编码的,所以不存在“ISO- 8859-1编码的String”或“GBK编码的String”这样的说法。而需要转换的唯一的原因是String进行了错误的编码。我们经常会碰到由ISO-8859- 1转换为诸如GBK/UTF-8等等这样的需求。所谓的转换过程是:String --> byte[] -->String。
也许 你非常清楚这个过程的代码:new String(text.getBytes("ISO-8859-1"),"GBK")。但是,要真正理解起来并不是那么简单。表面上看似乎很容易理解, 不就是将text String对象按照ISO-8859-1的方式编码为byte[]然后再把它按照GBK的方式转换为String吗?但是这句代码很容易会被误解为: “将text String由ISO-8859-1转换为GBK编码”,这种说法是错误的。难道你见过用这样的代码:new String(text.getBytes("GBK"),"UTF-8")来对String进行编码转换的吗?

之所以你会经常看到new String(text.getBytes("ISO-8859-1"),"GBK")这句代码,是因为一个GBK的字节流被错误地以ISO-8859-1的方式转换为String(unicode)了!发生这种情况最普遍的地方是一个GBK编码的网页向后台提交数据的时候,就有可能会看到这句代码的出 现。GBK的流被错误的当成ISO8859-1的流,所以便得到了一个错误的String。由于ISO8859-1是单字节编码,所以每个字节被按照原样 转换为String,也就是说,虽然这是一个错误的转换,但编码没有改变,所以我们仍然有机会把编码转换回来!所以那句经典的new String(text.getBytes("ISO-8859-1"),"GBK")便出现了。

如果系统误以为是其它编码格式,就有可能再也转换不回来了,因为编码转换并不是负负得正那么简单的 

 

五、 为什么中文的GBK编码第一个字节一定是负数(java)

GBK的编码是一个汉字两个字节,范围是第一个字节大于128开始,第二个字节从大于64开始。第一个字节大于127就表示这是个汉字的开始。byte这种类型只有java有,范围是-127到127.习惯上称第一字节为“高字节” ,而称第二字节为“低字节”。“高位字节”使用0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上0xA0)。 

以GBK字符集的第一个汉字“啊”字为例,它的区号16,位号01,则区位码是1601,计算后得到它的编码:0xB0=0xA0+16, 0xA1=0xA0+1。 B0的二进制是10110000,这里就超出了byte的范围,就会产生溢出,相当于10110000就成了补码,把10110000减1得10101111再按位取反得11010000(不包含符号位),这个是-80的原码,可以在代码中验证:

       byte[] test = "啊".getBytes("gbk");
        for (byte b : test) {
            System.out.println(Integer.toHexString(b));
        }

我们要判断一个字符是否是汉字得到它的GBK字节数组,再判断它的第一个字节是否为负就行了。

 

六、 unicode汉字编码表:

http://www.chi2ko.com/tool/CJK.htm

http://www.cnblogs.com/whiteyun/archive/2010/07/06/1772218.html

 

七、 参考地址:

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

 

http://blog.youkuaiyun.com/chinainvent/article/details/5550438

http://blog.sina.com.cn/s/blog_673c81990100t1lc.html

http://blog.youkuaiyun.com/soleghost/article/details/959832

http://hi.baidu.com/hellohzf/item/a22baf59ebd153c1d3e10ca0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值