同音不同字闹出大的笑话(邻桌问我一个汉字占几个字节)
邻桌手指在键盘上飞舞,忽然他抬起头很严肃地问我:一个汉字编码占用几个字节?我头也不抬很肯定的回答:两个!直到他连续重复问了三遍,我才发现有点不对劲,犹豫了下回答到:应该是两个吧。他笑了,笑声让我感到恐惧,一个入行级的问题问得我毫无自信,头皮发麻。于是,带着疑问好好梳理了下这个问题,结果发现自己被打脸!
先给出一种答案:中文在不同编码是不定长的 2~4个字节。
- 为什么需要编码
编码?刚入行的小伙伴可能会有一连串小问号,为什么需要编码?直接使用汉字或者数字或者英文字符不行吗?工欲善其事必先利其器,我们就先来介绍一下计算机中为什么需要编码。当今世界上共有233个国家和地区,其中包括197个国家和36个地区,这么多国家和地区,自然就有很多的语言,表达这些语言的符号就有很多,如果计算机全部采用这些这些符号,那计算机的压力就十分的巨大,而且要表达的符号太多,也无法简单地使用一个字节来表示。考虑到这些问题,借鉴多国之间语言存在差异,但是依然可以通过翻译来畅谈无阻,计算机也可以约定一套翻译的规则,在于计算机通信时,进项翻译,这个过程,可以理解为编码,编码之后又可以解码,还原成原来的内容。明白了这些之后,接下来就是了解有哪些编码规则,以及他们之间有什么区别。
常见的编码方式
(1)ASCII编码
美国制定的一套字符编码,ASCII码一共规定了128个字符的编码。在计算机内部,信息都可以表示成一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111。
(2)GB2312/GBK/GB18030编码
GB2312是由中国国家标准总局1980年发布的,1981年5月1日开始实施的一套国家标准,标准号是GB 2312—1980。GB2312编码适用于汉字处理、汉字通信等系统之间的信息交换,每个汉字及符号以两个字节来表示,但他不够全面,基本集共收入汉字6763个和非汉字图形字符682个,有一些罕见字并未收录,这就出现了后面的GBK及GB 18030。
GBK全称《汉字内码扩展规范》,又称国标码,1995年12月1日制订的,GBK是采用单双字节变长编码,英文使用单字节编码,完全兼容ASCII字符编码,中文部分采用双字节编码。共收录了21003个汉字。目前依然有一部分项目采用的是GBK编码。
GB18030,全称《信息技术中文编码字符集》,是中华人民共和国国家标准所规定的变长多字节字符集。其对GB 2312-1980完全向后兼容,与GBK基本向后兼容,并支持Unicode(GB 13000)的所有码位。GB 18030共收录汉字70,244个。GB 18030采用变长多字节编码,每个字可以由1个、2个或4个字节组成,他也是完全支持Unicode字符集的。
(3)Unicode字符集
Unicode 也叫万国码,Unicode 是一个标准,严格意义上定位的是字符集,Unicode 字符集为每一个字符分配一个编号,通过这个编号就能找到对应的字符。从这个定义也可以看出,Unicode 只是对各个语言字符进行了编号,并没有约定对编号如何存储以及存储几个字节等,而这些是由Unicode 字符集实现格式来约定的,比如下面提到的UTF-8、UTF-16、UTF-32等。
(4)UTF-8编码
UTF-8是Unicode编码的具体实现方式之一。它最主要的优点就是可变长度,字符编码通常由 1-4 个字节来表示,相比定长的实现方式,能够节省更多的空间。除此之外,UTF-8 兼容 ASCII,而Unicode的其他两种方式UTF-32 和 UTF-16 都不兼容 ASCII,因为它们没有单字节编码。
(5)UTF-32编码
UTF-32是Unicode编码的具体实现方式之一。一种固定长度的编码方案,不管字符编号大小,始终使用 4 个字节来存储;
(6)UTF-16编码
UTF-16是Unicode编码的具体实现方式之一。它介于 UTF-8 和 UTF-32 之间,使用 2 个或者 4 个字节来存储,长度既固定又可变。为什么说是既固定又可变呢?UTF-16对于 Unicode 编号范围在 0 ~ FFFF 之间的字符,UTF-16 使用两个字节存储,并且直接存储 Unicode 编号,不用进行编码转换,这跟 UTF-32 非常类似。对于 Unicode 编号范围在 10000~10FFFF 之间的字符,UTF-16 使用四个字节存储。 UTF-16没有指定后缀,即不知道其是大小端,所以其开始的两个字节表示该字节数组是大端还是小端。即FE FF表示大端,FFFE表示小端。故而又有两种细化的编码方式UTF-16BE和UTF-16LE。
UTF-16BE:其后缀是 BE 即 big-endian,大端的意思。大端就是将高位的字节放在低地址表示。
UTF-16LE:其后缀是 LE 即 little-endian,小端的意思。小端就是将高位的字节放在高地址表示。
- 汉字到底占用几个字节
可能有人会问,一个汉字占用几个字节为什么会衍生出上面那么多问题,正是因为使用不同的编码方式可能占用的字节数就不一样了。了解了编码方式之后就比较容易有一个清晰的概念。
中文在不同编码是不定长的 2~4个字节(至少两个字节,由汉字的总数超过6万字,2^16=65536)
(1) GBK编码,一个汉字占两个字节。
(2) UTF-16编码,通常汉字占两个字节,CJKV扩展B区、扩展C区、扩展D区中的汉字占四个字节(一般字符的Unicode范围是U 0000至U FFFF,而这些扩展部分的范围大于U 20000,因而要用两个UTF-16)。
(3) UTF-8编码是变长编码,通常汉字占三个字节,扩展B区以后的汉字占四个字节。
通过程序来试验一下:
控制台输出
按照上面的运行结果,“你”在UTF-16编码下占用4个字节。那么猜测两个汉字“你好”,在UTF-16编码下应该是8个字节,但是运行结果却和想象中的不一样,“你好”在UTF-16编码下共占6个字节。
运行结果好像和上面讲到的有点不相符啊!为什么会出现这样的结果的?
原因:java的字节码文件(.class)文件采用的是UTF-8编码,但是在java 运行时会使用UTF-16编码。在转码的时候会在前面加上表示字节顺序的字符,这个字符称为”零宽度非换行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。FEFF占用两个字节,所以就解释了为什么java环境下在UTF-16编码下,第一个汉字占4个字符,后面每个占用2个字符。
我们不妨将这些字符的在不同编码下的二进制转换为16进制并打印出来。将代码修改如下:
控制台输出:
通过对比之后,大家是不是有一个更加清晰的认识了呢?介绍完编码的这些知识,下次如果有人再问你同样的问题,我们就可以反问了:你说的是在哪种编码条件下呢?
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com