一、说明

        对于乱码问题,一直让人头痛;本人在工作中经常受乱码问题骚扰,因此,不了解一些码的常识,似乎已经无法说的下去,终于在参考恶补科普,因此写出下文以记录。

二、编码的发展历史

2.1 ASCII编码

        历史上最早出现的文字编码是ASC编码,ASCII(American Standard Code for Information Interchange,美国标准信息交换代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统,并等同于国际标准ISO/IEC 646。

        ASCII 码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。标准ASCII 码也叫基础ASCII码,使用7 位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。其中:

        1) 0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(响铃)等;通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等;ASCII值为8、9、10 和13 分别转换为退格、制表、换行和回车字符。它们并没有特定的图形显示,但会依不同的应用程序,而对文本显示有不同的影响。

        2)32~126(共95个)是字符(32是空格),其中48~57为0到9十个阿拉伯数字。

        3)65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。

        4)后128个称为扩展ASCII码。许多基于x86的系统都支持使用扩展(或“高”)ASCII。扩展ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号

下面为ASCII码表

ASCII码表

 

2.2 中文汉字GB系列编码简介

  • GB2312

        1980 年,中国发布了第一个汉字编码标准,也即 GB2312 ,全称 《信息交换用汉字编码字符集·基本集》,通常简称 GB (“国标”汉语拼音首字母), 共收录了 6763 个常用的汉字和字符,此标准于次年5月实施,它满足了日常 99% 汉字的使用需求

  • GBK

        由于有些汉字是在 GB2312 标准发布之后才简化的,还有一些人名、繁体字、日语和朝鲜语中的汉字也没有包括在内,所以,在 GB2312 的基础上添加了这部分字符,就形成了 GBK ,全称 《汉字内码扩展规范》,共收录了两万多个汉字和字符,它完全兼容 GB2312

        GBK 于 1995 年发布,不过它只是 "技术规范指导性文件",并不属于国家标准

  • GB18030

        GB18030 全称《信息技术 中文编码字符集》 ,共收录七万多个汉字和字符, 它在 GBK 的基础上增加了中日韩语中的汉字 和 少数名族的文字及字符,完全兼容 GB2312,基本兼容 GBK

        GB18030 发布过两个版本,第一版于 2000 年发布,称为 GB18030-2000,第二版于 2005 年发布,称为 GB18030-2005

2.3 Unicode字符集

1)产生和发展

        因为世界国家很多,每个国家都定义一套自己的编码标准,结果相互之间谁也不懂谁的编码,就无法进行很好的沟通交流,所以及时的出现了一个组织ISO(国际标准化组织)决定定义一套编码方案来解决所有国家的编码问题,这个新的编码方案就叫做Unicode。

        Unicode目前国际通用的字符集叫Unicode(又叫万国码、统一码),全世界所有民族地区的文字基本都包含在里面。它的出现是为了解决世界各地用不同的一套字符集,然后计算机软件无法兼容共用的问题,造福了软件开发者,也方便了用户。下图就是Unicode字符集中汉字(CJK)码表的样子,每个汉字都有个号码,比如4E00就是汉字“一”的号码。

        Unicode刚开始每个字符用2个字节来表示,值范围0x0000~0xFFFF,共可以表示65536个字符。后来2个字节发现不够用,比如生僻字、网络流行的电子表情符号emoji等,因此Unicode字符集不停地扩充,字符集越来越庞大,现在的Unicode字符集的编码空间扩充到3个字节,最大值为0x10FFFF,某些新字符需要3个字节才能表示。

2)目前的结构——平面(Plane)和块(Block)
        Unicode扩充之后,为了便于描述和统一分配,就有了一个新的概念:平面(Plane)。除了之前的FFFF两个低字节用来表示字符的码,第三个字节的值用来表示平面编号。比如Unicode码0x10FFFF中的0x10,就是平面编号,因此,现在的Unicode有0-16共17个平面,每个平面可以容纳FFFF个文字,所以:

        理论上Unicode容纳的字符数量0x10FFFF = 平面数量 × 每平面容量 = 17 × 65536 = 1114112个

        平面又包含了块(Block)的概念,一个块就是一个字节的空间,最多能表示256个字符。一个平面是2个字节的空间,最大FFFF,低位的FF是一个块,高位的FF用来作为块的编号。所以:

        理论上一个平面字符数量 = FF个块 × 每个块FF个字符 = 256 ×256 = 65536个字符
3)基本平面----BMP平面

        Unicode最重要、最常用的平面是第一个平面,简称BMP(Basic Multilingual Plane ),编号为0,其实也就是最早的那65535个文字,其中已经包含了世界各民族的常用文字。其中,汉字(CJK)是占空间最多的,下图浅红色从34一直到9F共107个块,每个块又能表示256个字符,所以
 

2.4  UTF-8编码

        由于Unicode中过于稀疏,比较浪费网络带宽和硬盘,因此为了解决这个问题,就在Unicode的基础上,定义了一套编码规则(将「码位」转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程)),这个新的编码规则就是UTF-8,采用1-4个字符进行传输和存储数据。因此,UTF-8不是一套编码,而是Unicode的一套压缩和解压缩的机制。 

2.5 UTF-8和Unicode转换

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

  •   较早的编码规则:使用下面的模板进行转换

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的编码表如下:

        比如汉字“智”,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.6 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”

2.7 UTF-8和Unicode与GBK的关系

        utf-8--------decode(解码)----->Unicode类型<-------decode(解码)-----gbk

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

2.8  乱码出现的原因

        UTF是UCS标准(Universal Character Set)和Unicode码在计算机中的编码格式,用4个字节或者2个字节来表示一个字符,即UTF32(UCS-4)、UTF-16(UCS-2)。由于大部分常用字符高位都是0,只需要16位就能表示(常用字符都位于BMP)。为了节约空间就有了UTF-16,用2个字节表示一个字符,所以UTF-16是UTF-32的子集。大家常说的形如u'\u4f60'的Unicode,编码格式其实是UTF-16。

        但是UTF-32和UTF-16有一个很严重的问题——和C语言不兼容。因为C语言中0000 0000表示字符串结尾,而UTF-32和UTF-16中有很多字符高位都是0,和字符串结尾冲突了。此时,UNIX之父Ken Thompson提出的UTF-8编码完美解决了这个问题。所以UTF-8和UTF-32、UTF-16相同,也是Unicode的一种编码格式。我们前面说的Unicode和UTF-8转换,其实不准确。准确地说是UTF-32、UTF16转换为UTF-8。

05-17 23:07