程序员必备:必须弄懂的字符编码
程序开发常见的ASCII、GB2312、GBK、GB18030、UTF8、ANSI、Latin1中文编码到底有何不同?如果你在业务中也曾经被乱码搞晕过,不妨一起探究一下。
一、字符编码做的事
在计算机眼里读到的所有文字都是由0和1组成的字符串。
让我们想想为了能让汉字正常显示在屏幕上,我们需要怎么做:
- 给所有的汉字一个独一无二的数字编号,做一个数字编号到汉字的mapping关系(即字符集)
- 把这个数字编号能用0和1表示出来
我们设计一个简单的中文编码吧
-
第一步:规定在中文占用的字节个数
如何设计一个中文占用几个字节?大家都明白一个字节8位那么最多可能表示2^8=255个字符,现在中文收录大概有91251,
那么我们就规定每个中文占用2个字节,占用位数不够的前面用0补齐
。当收到报文段应该如何拆分呢?那么我们规定
第一个字节的前两位二进制固定为10,第二个字节的第一位固定为0
-
第二步:数字与中文映射Mapping
编码1号中文:"我" ,10 000000 - 0 0000001
编码3号中文:"你" ,10 000000 - 0 0000011
编码9号中文:"爱" ,10 000000 - 0 0001001
.........
编码号n中文:"结" ,10 011111 - 0 111 0011
-
第三步:翻译报文成中文
如我们收到了下面的0 1报文应该如何翻译成中文呢?
因为我们有规定每个中文字的第一个字节固定以10开头这样才不能正确的得分割报文。大家可以想想如果我们不在第一步规定固定分割符会怎么样?
从上面的一个简单例子我们大概理解了字符和我们的编码是如何显示的,但是实际的编码处理会比这个更为复杂。
二、编码概述
1. 字符
字符(Character):具有语义价值的最小文本单位
。说的简单点字符是各种文字和符号的总称。一个字符可以是一个中文汉字、一个英文字母、一个阿拉伯数字、一个标点符号、一个图形符号或者控制符号等。
2. 字符集
字符集(Character Set):是指多个字符的集合。
如:拉丁字符集被英语和大多数欧洲语言使用,而希腊字符集仅被希腊语言使用。不同的字符集包含的字符个数不一样、包含的字符不一样、对字符的编码方式也不一样。
3. 字符编码
字符编码(Character Encoding):字符编码是指一种映射规则,每个字符对应一个唯一的数字
,根据这个映射规则可以将某个字符映射成其他形式的数据以便在计算机中存储和传输。
4. 码点
码点(Code Point):有些地方翻译为码值或内码。是指在某个字符集中,根据某种编码规则将字符编码后得到的值
。比如在ASCII字符集中,字母A经过ASCII编码得到的值是65,那么65就是字符A在ASCII字符集中的码点。
三、ASCLL编码
上个世纪60年代,美国制定了一套字符编码规则,对英语字符与二进制位之间的关系做了统一规定,这编码规则被称为ASCII编码,一直沿用至今。
ASCII编码一共规定了128个字符的编码规则 ,这128个字符形成的集合就叫做ASCII字符集。在ASCII编码中,每个字符占用一个字节的后面7位,最前面的1位统一规定为0
。
在ASCII编码中,031 是控制字符如换行回车删除等,32126 是可打印字符,可以通过键盘输入并且能够显示出来。(下图是ASCII字符集中字符和码值的对应关系)
扩展ASCII编码(extended the ASCII)
由于ASCII是针对英语设计的,当处理带有音调标号(形如汉语的拼音)的欧洲文字时就会出现问题。为了表示更多字符,人们打起了校验码的注意,让最高位也用作字符表示,这就是ASCII码扩字符集。ASCII扩展的字符集包括255个字符。
欧洲的语言体系有个特点:小国家特别多,每个国家可能都有自己的语言体系,语言环境十分复杂。因此即使EASCII可以表示256个字符,也不能统一欧洲的语言环境。
形成了很多子标准:ISO-8859-1、ISO-8859-2、ISO-8859-3、......、ISO-8859-16。这些子标准适用于欧洲不同的国家地区。
具体关于ISO-8859的标准请参考这个链接。
这边我摘录了部分介绍。
ISO8859-1 字符集
Latin-1,是西欧常用字符,包括德法两国的字母。
ISO8859-2 字符集,也称为 Latin-2,收集了东欧字符。
ISO8859-3 字符集,也称为 Latin-3,收集了南欧字符。
至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。比如,简体中文常见的编码方式是GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示65536个符号。
一个字节最多只能表示256种符号,肯定是不够的,必须使用多个字节表达一个符号, 因此才出现了后面的Unicode字符集和GB2312等字符集。
四、汉语编码
汉字编码根据应用目的的不同,大致可分为如下编码:
- 外码: 也称输入码,是用来将汉字输入到计算机中的一组键盘符号,如拼音码、五笔字型码等。
- 交换码(国标码) :中国标准总局1981年制定了中华人民共和国国家标准GB2312--80《信息交换用汉字编码字符集--基本集》,即国标码,除了GB2312编码还有GBK编码和GB18030编码,是一个四位十六进制数,不是从0x0000开始的,而是从0x2020开始的。区位码则是国标码另一种表现形式,是一个四位的十进制数,把国标GB2312--80中的汉字、图形符号组成一个94×94的方阵,分为94个区(01-94),每区包含94个位(01-94),类似数组的数组,全部元素或位置=94×94=8836个,其中7445个位置分配给汉字和图形字符,其余的1391个位置保留备用。
- 机内码:是区位码(国标码、交换码)在计算机内部的二进制具体表示、GB内码的存储格式始终是big endian,即高位在前。
- 字形码:谈不上说是什么具体的码值,只不过是个约定成俗的叫法而已,其实是一个图形点阵结构(常用是16×16点阵)。
- 地址码:汉字地址码是指字库中逻辑地址码,与汉字内码有着简单的对应关系易于相互转换。
常见国标码有以下几种:
- GB 2312-1980:信息交换用汉字编码字符集、国标码的基本集、7445个字符、CP936
- GBK-1995:汉字内码扩展规范、21886个字符、CP936
- GB13000.1-1993:通用多八位编码字符集用于信息技术
- GB18030-2000:信息交换用汉字编码字符集、基本集的扩充、27484字符、CP54936、不支持手机与MP3
GBK编码和GB18030编码
GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0(此编码为微软为简体中文用户设计的),全称《汉字内码扩展规范》(GBK),英文名称 Chinese Internal Code Specification,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号,共收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。
我们国家是个多民族国家,各个民族几乎都有自己独立的语言系统,为了表示那些字符,于是2000年的GB18030取代GBK1.0正式成为国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。
中文编码之间存在兼容性
这也是平时业务中最常见的导致乱码场景,使用UTF8去读取GBK编码的文字,可能会看到各种乱码。
GB系列的几种编码,GB18030兼容GBK,GBK又兼容GB2312
五、Unicode编码
对于全世界各国语言都不同很难全部包含在内,上面说的编码太多太过于杂乱所以就出现了Unicode字符集。Unicode赋予了全世界所有文字和符号一个独一无二的数字编号
它定义了一种统一的多语言文本编码方式,使文本数据能够在国际上交换,并为全球软件奠定了基础。
Unicode是通用字符编码(universal character encoding)标准,由ISO(The International Organization for Standardization)与Unicode联盟(Unicode Consortium)共同开发与维护。
之所以称之为Unicode而不ISO 10646,只不是Unicode这个名字更容易记住而已。
Unicode三种编码
Unicode标准定义了三种编码形式(UTF-8、UTF-16、UTF-32),允许以字节、字或双字为的格式(即代码单位是8位、16位或32位)存储和传输相同的数据。
-
UTF-8介绍
UTF-8:以8位(byte)字节位基本单元的可变长度编码,根据字符的需要,将每个字符编码可以是1-4个字节。
UTF-8数据兼容ASCII数据,ASCII字符都以UTF-8编码为一个字节,该字节与ASCII中的编码相同。后面会讲UTF的编码格式。
UTF-8数据与GB码不兼容。例如"汉"字的Unicode编码是6C49,而GB码是BABA。
BMP中的所有字符都以UTF-8编码为一个、两个、三个字节。其他平面中的字符以UTF-8编码为四个字节。
UTF-8有三个主要优点:
它完全兼容ASCII,ASCII数据可以被视为UTF-8数据。
它不要求所有字符都使用两个或更多字节的存储空间。
统一的多语言文本编码方式。
-
UTF-16
UTF-16:16位可变长度编码。 UCS-2的一种扩展,若要引用BMP以外的平面上的字符,则通过组合两个UCS-2代码单元(code unit)来指定单个非BMP字符。
-
UTF-32
UTF-32:32位固定长度编码,相当于UCS-4。
UTF-8 的编码规则
UTF-8 的编码规则如下:
- 对于单字节的字符,字节的第一位设为 0,后面 7 位为其代码点。
- 对于 n 字节的符号( n > 1),第一个字节的前 n 位都设为 1,第 n + 1 位设为 0,后面字节的前两位一律设为 10 。剩下的没有提及的二进制位,全部为为其代码点。
下图是字符的UTF-8编码的示例:
每个字符在UTF-8中表示为最多4个字节的序列,其中第一个字节表示多字节序列中要遵循的字节数,从而实现高效的数据解析。
数据库与Unicode
MySQL支持两种UTF-8变体:
- utf8mb4:这是自MySQL 5.5以来支持的完整UTF-8字符集
- utf8:也称为utf8mb3。这只支持Unicode 3.0的基本多语言平面,不支持4字节字符。
在MySQL中,按列使用字符集。新列的默认字符集设置在表级别。表的默认值是在数据库级别设置的。