小白在刚开始学习编程,经常会遇到一些乱码问题,导致程序无法编译,在这里,对此进行一个了解。 

    在聊编码集之前,我们先来了解一些名词解释:

  •     字符集:所谓字符编码就是一个系统支持的所有抽象字符的集合,也就是说我们平常使用的文字,标点符号,图形符号等都是字符集。但计算机无法识别这些文字,只能识别二进制的数字系统,所以需要一套规则,将字符转换成为数字系统,这就是字符编码。
  •    编码: 按照某种字符编码规则( GBK, UTF-8 等) 将字符以二进制序列形式存储在计算机中. 
  •     解码: 将存储在计算机中的二进制序列数据解析成对应的字符的过程. 

乱码问题及字符编码集(一)

   上图表示的是程序运行过程中对字符进行编码和解码的过程的.   请注意 unicode 的重要作用( java语言在代码里声明的每一个char、String类型的变量中字符在在JVM的常量池或磁盘文件中,都是以unicode格式存在的.)  为什么呢?这就要从字符集的发展历史说起了.  

编码集发展历史: 
1. ASCII编码:  

     美国国家标准协会ANSI制定了一个标准,规定了常用字符的集合以及每个字符对应的编号,这就是ASCII字符集(Character Set),也称ASCII码。这个编码集如下,只是一个简单的查表的过程。比如我要存 'a'字符,只要在ASCII表中查到它对应的编码97(或二进制  0110 0001 ),再以二进制流写入存储设备即可. 

乱码问题及字符编码集(一)

  2。 OEM字符集

    请注意: 此时,ASCII码表只用到了一个字节(八位)中的前七位,第八位没有用到(所以它只能表示128个字符,后面128个数字没有用到).   这样混乱就产生,每个人都可以在 128-255这些位置存放到不同的字符,形成了各种不同的字符集,这些字符集统称为OEM字符集,用这些字符集生成的文档就不容易实现互换了. 

3. ISO-8859-1 ( Latin-1 )

   在这其中 ASCII最优秀的扩展方案就是 ISO-8859-1,通常称为Latin-1的OEM字符集,它包括了足够的附加字符集来写基本的西欧,希腊语等语言。( 我们安装mysql 5时,就可以注意到,它默认安装的就是latin-1 的字符集 ).

4.  ANSI标准

      最后,这个人人参与的OEM终于以ANSI标准的形式形成文件。在ANSI标准中,每个人都认同如何使用低端的128个编码,这与ASCII相当一致。不过,根据所在国籍的不同,处理编码128以上的字符有许多不同的方式。这些不同的系统称为代码页。

   讲到这里,大家可以注意到,上面我们提到的字符集都是基于单字节编码,也就是说,一个字节翻译成一个字符。这对于拉丁语系国家来说可能没有什么问题,因为他们通过扩展第8个比特,就可以得到256个字符了,足够用了。但是对于亚洲国家来说,256个字符是远远不够用的。因此这些国家的人为了用上电脑,又要保持和ASCII字符集的兼容,就发明了多字节编码方式,相应的字符集就称为多字节字符集。例如中国使用的就是双字节字符集编码(DBCS,Double Byte CharacterSet)

5. 双字节字符集编码(DBCS,Double Byte CharacterSet)

     对于多字节字符集,代码页中通常会有很多码表。那么程序怎么知道该使用哪张码表去解码二进制流呢?答案是,根据第一个字节来选择不同的码表进行解析。   比如Windows系统采用936代码页来实现对GBK字符集的编解码。在解析字节流的时候,如果遇到字节的最高位是0的话,那么就使用936代码页中的第1张码表进行解码,这就和单字节字符集的编解码方式一致了(如下图)。

乱码问题及字符编码集(一)

      当字节的高位是1的时候,确切的说,当第一个字节位于0x81–0xFE之间时,根据第一个字节不同找到代码页中的相应的码表,例如当第一个字节是0x81,那么对应936中的下面这张码表:按照936代码页的码表,当程序遇到连续字节流0x81 0x40的时候,就会解码为“丂”字符。

乱码问题及字符编码集(一)

6. GB2312与GBK有什么不同。     

    它们都占两个字节,但GB2312编码组合出7000多个常用简体汉字,还包括数字符号、罗马希腊字母、日文假名等. 但GB2312并没有把所有的码位都用完.

    而GBK是GB2312的扩展(K就是扩展的意思). GBK包括了GB2312的所有内容,同时增加了近20000个新的汉字(包括繁体)和符号 。只要求高位大于0x7F,低位可以小于0x7F,认为是中文。

7. Unicode ( 定长 , 通常使用2个字节,有的是4个字节 , 具体如何以几个字节编码由字符编码(utf-8, utf-16决定).

     虽然通过使用不同字符集,我们可以在一台机器上查阅不同语言的文档,但是我们仍然无法解决一个问题:在一份文档中显示所有字符( 这就是乱码的由来, 世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。) 。为了解决这个问题,我们需要一个全人类达成共识的巨大的字符集,这就是Unicode字符集。

Unicode字符集将所有字符按照使用上的频繁度划分为17个层面(Plane),每个层面上有216=65536个字符码空间。

   其中第0个层面BMP,基本涵盖了当今世界用到的所有字符。其他的层面要么是用来表示一些远古时期的文字,要么是留作扩展。我们平常用到的Unicode字符,一般都是位于BMP层面上的。目前Unicode字符集中尚有大量字符空间未使用。

    前面谈到的ASCII,GB2312,GBK字符集都限定了最多2个字节来编码所有字符,并且规定了字节序。这样的编码系统通常用简单的查表,也就是通过代码页就可以直接将字符映射为存储设备上的字节流了. 即这些编码的字符集与字符编码方案(码表)是合在一起了,限制了它的扩展能力。 

    而unicode在设计上考虑到了这一点,将字符集和字符编码方案分离开

乱码问题及字符编码集(一)

注意左边表示每个字符在unicode字符中都能找到唯一确定的编号(unicode编码),但最终如何以字节流编码和反编码的是具体的字符编码( 如 UTF-8, UTF-16等) . 例如同样是对Unicode字符“A”进行编码,UTF-8字符编码得到的字节流是0x41,而UTF-16(大端模式)得到的是0x00 0x41. 

概念上要区分开,以前的GB2312,ASCII即是字符编码方式(这决定如何存,如何读取解析),也是字符集( 表示字符与编号的映射关系),而从unicode开始,它只定了字符的集合和编号,具体如何编码由字符编码方案决定(即 utf-8,utf-16等).

8. utf-8   :变长,所以可以节省空间. 

     utf-8的特点是它是变长存储,所以适用于存储和网络传输,是具体的字符编码方案. 

  •          国际标准组织(ISO)制定英文字符使用1个字节,沿用原来的ASCII码
  •         使用1~4个字节表示一个符号,中文存储使用3个字节(ascii码中的内容用1个字节保存\欧洲的字符, 用2个字节保存\东亚的字符用3个字节保存\特殊符号用4个字节)
  •         Unicode是内存编码表示方案(规范),而utf-8是如何保存和传输Unicode的方案(实现)
     

UTF-8的编码规则:

1)对于单字节的UTF-8编码,该字节的最高位为0,其余7位用来对字符进行编码(等同于ASCII码)。

2)对于多字节的UTF-8编码,如果编码包含 n 个字节,那么第一个字节的前 n 位为1,第一个字节的第 n+1 位为0,该字节的剩余各位用来对字符进行编码。在第一个字节之后的所有的字节,都是最高两位为"10",其余6位用来对字符进行编码。

        > 优点:虽然内存汇总的数据都是Unicode,但当数据保存到磁盘或者用于网络传输时,使用utf-8会节省更
多的流量和硬盘空间。UTF8编码后的大小是不一定,例如一个英文字母"a" 和一个汉字 "好",编码后占用的空间大小就不样了,前者是一个字节,后者是三个字节!

总结: 通过上面的讲解,现在在回过头去,体会一下最前面的那张程序编解码运行图,大家能看懂它的意思了吗. 

相关文章: