字符集简介
我们知道在计算机中只能存储二进制数据,那该怎么存储字符串呢?当然是建立字符与二进制数据的映射关系了,建立这个关系最起码要搞清楚两件事儿:
-
你要把哪些字符映射成二进制数据?
也就是界定清楚字符范围。
-
怎么映射?
将一个字符映射成一个二进制数据的过程也叫做
编码
,将一个二进制数据映射到一个字符的过程叫做解码
。
人们抽象出一个字符集
的概念来描述某个字符范围的编码规则。比方说我们来自定义一个名称为xiaohaizi
的字符集,它包含的字符范围和编码规则如下:
-
包含字符
'a'
、'b'
、'A'
、'B'
。 -
编码规则如下:
采用1个字节编码一个字符的形式,字符和字节的映射关系如下:
'a' -> 00000001 (十六进制:0x01) 'b' -> 00000010 (十六进制:0x02) 'A' -> 00000011 (十六进制:0x03) 'B' -> 00000100 (十六进制:0x04)
有了xiaohaizi
字符集,我们就可以用二进制形式表示一些字符串了,下边是一些字符串用xiaohaizi
字符集编码后的二进制表示:
'bA' -> 0000001000000011 (十六进制:0x0203)
'baB' -> 000000100000000100000100 (十六进制:0x020104)
'cd' -> 无法表示,字符集xiaohaizi不包含字符'c'和'd'
比较规则简介
在我们确定了xiaohaizi
字符集表示字符的范围以及编码规则后,怎么比较两个字符的大小呢?最容易想到的就是直接比较这两个字符对应的二进制编码的大小,比方说字符'a'
的编码为0x01
,字符'b'
的编码为0x02
,所以'a'
小于'b'
,这种简单的比较规则也可以被称为二进制比较规则,英文名为binary collation
。
二进制比较规则是简单,但有时候并不符合现实需求,比如在很多场合对于英文字符我们都是不区分大小写的,也就是说'a'
和'A'
是相等的,在这种场合下就不能简单粗暴的使用二进制比较规则了,这时候我们可以这样指定比较规则:
- 将两个大小写不同的字符全都转为大写或者小写。
- 再比较这两个字符对应的二进制数据。
这是一种稍微复杂一点点的比较规则,但是实际生活中的字符不止英文字符一种,比如我们的汉字有几万之多,对于某一种字符集来说,比较两个字符大小的规则可以制定出很多种,也就是说同一种字符集可以有多种比较规则,我们稍后就要介绍各种现实生活中用的字符集以及它们的一些比较规则。
一些重要的字符集
不幸的是,这个世界太大了,不同的人制定出了好多种字符集
,它们表示的字符范围和用到的编码规则可能都不一样。我们看一下一些常用字符集的情况:
-
ASCII
字符集共收录128个字符,包括空格、标点符号、数字、大小写字母和一些不可见字符。由于总共才128个字符,所以可以使用1个字节来进行编码,我们看一些字符的编码方式:
'L' -> 01001100(十六进制:0x4C,十进制:76) 'M' -> 01001101(十六进制:0x4D,十进制:77)
-
ISO 8859-1
字符集共收录256个字符,是在
ASCII
字符集的基础上又扩充了128个西欧常用字符(包括德法两国的字母),也可以使用1个字节来进行编码。这个字符集也有一个别名latin1
。
GB2312
字符集
收录了汉字以及拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母。其中收录汉字6763个,其他文字符号682个。同时这种字符集又兼容ASCII
字符集,所以在编码方式上显得有些奇怪:
- 如果该字符在
ASCII
字符集中,则采用1字节编码。 - 否则采用2字节编码。
这种表示一个字符需要的字节数可能不同的编码方式称为变长编码方式
。比方说字符串'爱u'
,其中'爱'
需要用2个字节进行编码,编码后的十六进制表示为0xB0AE
,'u'
需要用1个字节进行编码,编码后的十六进制表示为0x75
,所以拼合起来就是0xB0AE75
。
我们怎么区分某个字节代表一个单独的字符还是代表某个字符的一部分呢?别忘了
ASCII
字符集只收录128个字符,使用0~127就可以表示全部字符,所以如果某个字节是在0~127之内的,就意味着一个字节代表一个单独的字符,否则就是两个字节代表一个单独的字符。
-
GBK
字符集GBK
字符集只是在收录字符范围上对GB2312
字符集作了扩充,编码方式上兼容GB2312
。 -
utf8
字符集收录地球上能想到的所有字符,而且还在不断扩充。这种字符集兼容
ASCII
字符集,采用变长编码方式,编码一个字符需要使用1~4个字节,比方说这样:'L' -> 01001100(十六进制:0x4C) '啊' -> 111001011001010110001010(十六进制:0xE5958A)
其实准确的说,utf8只是Unicode字符集的一种编码方案,Unicode字符集可以采用utf8、utf16、utf32这几种编码方案,utf8使用1~4个字节编码一个字符,utf16使用2个或4个字节编码一个字符,utf32使用4个字节编码一个字符。更详细的Unicode和其编码方案的知识不是本书的重点,大家上网查查哈~
MySQL中并不区分字符集和编码方案的概念,所以后边唠叨的时候把utf8、utf16、utf32都当作一种字符集对待。
对于同一个字符,不同字符集也可能有不同的编码方式。比如对于汉字
'我'
来说,ASCII
字符集中根本没有收录这个字符,utf8
和gb2312
字符集对汉字我
的编码方式如下:utf8编码:111001101000100010010001 (3个字节,十六进制表示是:0xE68891) gb2312编码:1100111011010010 (2个字节,十六进制表示是:0xCED2)
MySQL中的utf8和utf8mb4
我们上边说utf8
字符集表示一个字符需要使用1~4个字节,但是我们常用的一些字符使用1~3个字节就可以表示了。而在MySQL
中字符集表示一个字符所用最大字节长度在某些方面会影响系统的存储和性能,所以设计MySQL
的大叔偷偷的定义了两个概念:
utf8mb3
:阉割过的utf8
字符集,只使用1~3个字节表示字符。utf8mb4
:正宗的utf8
字符集,使用1~4个字节表示字符。
有一点需要大家十分的注意,在MySQL
中utf8
是utf8mb3
的别名,所以之后在MySQL
中提到utf8
就意味着使用1~3个字节来表示一个字符,如果大家有使用4字节编码一个字符的情况,比如存储一些emoji表情啥的,那请使用utf8mb4
。