Java基础之终于弄懂了字节、字符、字符集和字符编码

目录

一、如何理解概念

二、乱码的真相

三、如何转换

四、总结

 


 

关于字节、字符、字符集和字符编码的问题,根据以下几篇文字,做以一下整理,原文链接如下:

https://blog.csdn.net/longwen_zhi/article/details/79704687

https://blog.csdn.net/softwarenb/article/details/51994943

https://blog.csdn.net/kdc18333608478/article/details/70214493

一、如何理解概念

字节

     也就是Byte,是一个计量单位。数据存储是以字节为单位的,也就是常说的1KB、1M、1G等,而数据传输是以“位”(bit)为单位,一个位也就是二进制中的0或1,每8个位组成一个字节,对应该关系也就是1Byte=8bit。

字符

    字符是字母、标点符号、数字的统称,汉字也是一种字符,是一种表现形式,字符在计算机上也是以二进制的形式存储的,单位是字节。 一个字符需要多少字节存储,和字符集编码有关。

  • 在 ASCII 编码中,一个英文字母字符存储需要1个字节。
  • 在 GB2312 编码或 GBK 编码中,一个汉字字符存储需要2个字节。
  • 在UTF-8编码中,一个英文字母字符存储需要1个字节,一个汉字字符储存需要3到4个字节。

字符集

    字符集是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集有:ASCII字符集、ISO 8859字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。

字符编码

  1.  计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。 
  2.  字符编码(encoding)和字符集不同。字符集只是字符的集合,不一定适合作网络传送、处理,有时须经编码(encode)后才能应用。如Unicode可依不同需要以UTF-8、UTF-16、UTF-32等方式编码。 
  3. 字符编码就是以二进制的数字来对应字符集的字符。 因此,对字符进行编码,是信息交流的技术基础。

二、乱码的真相

字符集:

ASCII

         我们知道,计算机内部,所有信息最终都是一个二进制值。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111。上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为 ASCII 码,一直沿用至今。

       ASCII 码一共规定了128个字符的编码,比如空格SPACE是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的一位统一规定为0。

ASCII编码演变过程

        英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。

        但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0--127表示的符号是一样的,不一样的只是128--255的这一段。

      至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是 GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示 256 x 256 = 65536 个符号。

GBK的由来

       由于ASCII编码不支持中文,因此,当中国人用到计算机时,就需要寻求一种编码方式来支持中文。于是,国人就定义了一套编码规则:当字符小于127位时,与ASCII的字符相同,但当两个大于127的字符连接在一起时,就代表一个汉字,第一个字节称为高字节(从0xA1-0xF7),第二个字节为低字节(从0xA1-0xFE),这样大约可以组合7000多个简体汉字。这个规则叫做GB2312
        但是由于中国汉字很多,有些字无法表示,于是重新定义了规则:不在要求低字节一定是127之后的编码,只要第一个字节是大于127,就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。这种扩展之后的编码方案称之为GBK标,包括了GB2312的所有内容,同时新增了近20000个新的汉字(包括繁体字)和符号。 但是,中国有56个民族,所以,我们再次对编码规则进行了扩展,又加了近几千个少数民族的字符,于是再次扩展后得编码叫做GB18030。中国的程序员觉得这一系列编码的标准是非常的好,于是统统称他们叫做"DBCS"(Double Byte Charecter Set 双字节字符集)。
 

Unicode

         正如上一节所说,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是 Unicode,就像它的名字都表示的,这是一种所有符号的编码。

     Unicode 当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字严。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表。
Unicode 的问题:Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。每个字符必须使用2个字节甚至更多的字节数,即用16位二进制来表示所有的字符,对于ASCII编码表里的字符,保持其编码不变,只是将长度扩展到了16位,其他国家的字符全部统一重新编码。由于传输ASCII表里的字符时,实际上可以只用一个字节就可以表示,所以,这种编码方案在传输数据比较浪费带宽,存储数据比较浪费硬盘。所以急需一种统一更有效的编码规则,UTF应运而生。

UTF-8

UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8 的编码规则很简单,只有二条:

  1. 对於单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
  2. 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
Unicode符号范围(十六进制) |  UTF-8编码方式(二进制)
------------------------------------------------------------------------
0000 0000-0000 007F            |     0xxxxxxx
0000 0080-0000 07FF            |     110xxxxx 10xxxxxx
0000 0800-0000 FFFF            |     1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF            |     11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
————————————————


        utf-8区分每个字符的开始是根据字符的高位字节来区分的,比如用一个字节表示的字符,第一个字节高位以“0”开头;用两个字节表示的字符,第一个字节的高位为以“110”开头,后面一个字节以“10开头”;用三个字节表示的字符,第一个字节以“1110”开头,后面俩字节以“10”开头;用四个字节表示的字符,第一个字节以“11110”开头,后面的三个字节以“10”开头。

       其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示)

ISO8859-1

IS0-8859-1是Java网络传输使用的标准字符集,属於单字节编码,最多能表示的字符范围是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"。很明显,这种表示方法还需要以另一种编码为基础。Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。

三、如何转换

1、UTF-8和Unicode转换
     比如汉字“智”,utf-8编码是“\xe6\x99\xba”对应的二进制为:“111001101001100110111010”,由于utf-8中一个汉字是3个字节,所以对应的模板为“0000 0800-0000 FFFF |  1110xxxx 10xxxxxx 10xxxxxx”。

11100110  10011001    10111010
1110xxxx  10xxxxxx    10xxxxxx
0110      011001      111010

0110011001111010代表十六进制667A,因此根据规则转换得出“智”Unicode的位置为为“667A”。

同样,根据Unicode中字符的编码位置,也能找到对应的utf-8编码。

2、Unicode与GBK编码的转换
    比如汉字“路”,在gbk中的编码为“\xc2\xb7”,对应的二进制为:“1100 0010 1011 0111”。同时“路”在Unicode字符集中的位置是“\u8def”(python中的Unicode类型),因此可以通过“\u8def”在Unicode字符集中找到“路”对应的编码为“4237”,对应的二进制为:“0100 0010 0011 0111”,由于gbk的俩个字节的高字节是为了区分中文和ASCII,所以将“1100 0010 1011 0111”高字节的“1”去掉后,就对应Unicode字符集中的0100 0010 0011 0111”

3、UTF-8和Unicode与GBK的关系
utf-8--------decode(解码)----->>Unicode类型<<-------decode(解码)-----gbk

utf-8<<--------encode(编码)----->>Unicode类型<<-------encode(编码)----->>gbk


四、总结

 整理完,写个小总结:

     随着计算机的不段发展,因为地域语音的差异,出现了适应不同语言表示的字符集,而不同的字符集也有不同的编码规则,文中梳理的最基础的字符集ASCII,其他字符集都是依据此进行的演变,也随之衍生出对应的编码规则和方式,这也就是造成乱码的真正原因。

   如何避免乱码问题以及遇到乱码问题该如何分析:

  • 保证数据的出口和入口采用的编码方式一致,如果不一致要转换编码方式。
  • 有时候系统默认编码或者应用默认编码也会是造成乱码的原因,比如:Windows默认编码是GBK,Linux的默认编码为UTF-8。
  • UTF-8是个不错的选择

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章