我对字符集和字符编码的理解
先定义两个概念。
字符集
字符集就是把字符和一串数字(码点)一一对应起来。GB2312,GBK,UNICODE,这些都是字符集。
字符编码
字符编码就是取得字符集中和字符对应的那串数字(字符编码)之后,基于取得的那串数字再生成另外一串数字。utf8,utf16,utf32这些都是字符编码。
字符集和字符编码的关系
我们可以把字符集看作一个函数,F(字符)=string1
我们也可以把字符编码看作另外一个函数,G(F(字符))=string2
string1即可以等于string2,也可以不等。
当string1等于string2的时候,意味着字符集的码点就是字符编码。比如GBK,GB2312
当string2不等于string2的时候,意味着字符集的码点和字符编码是不相同的,比如一个汉字的unicode字符集的结果是和utf8编码的结果是不一样的。
举个例子:
飞的unicode码点是0x98DE,这个网站可以查询:https://bianma.supfree.net/
下面是一张utf8编码规则表:
0x98DE在0000 0800 -0000 FFFF这个区间内,所以应该用第三行编码规则。
先把0x98DE转化为二进制:1001 1000 1101 1110 共有16个二进制数字。
1110xxxx 10xxxxxx 10xxxxxx ,这里面共有16个x,就是16个空,我们把上面那串数字按照顺序填写进来就可以了。
1110100110100011 10011110 ,转化为16进制就是0xE9A39E。所以飞的utf8编码就是0xE9A39E,经过上面那个网站查询,发现是完全正确的。
再举个例子
飞的GBK编码,经过网站查询飞在字符集中的码点是0xB7C9,那飞的GBK编码就是0xB7C9。
为什么UNICODE字符集的码点不能作为字符编码?
直接把UNICODE字符集的码点拿来当作字符编码就可以了嘛,为啥非要再折腾一下呢?因为UNICODE是兼容ASCII码的,ASCII码是一个字节对应一个字符,而如果把UNICODE字符集码点当作字符编码,UNICODE要编码那么多的字符,汉字、日文,必然会存在多个字节对应一个字符的情况,因为一个字节只能表示256种情况。好,我现在给你三个连续的字节,你怎么解码呢?你是把这三个字节依次当作三个字符解码呢?还是把第一个字节当作一个字符解码后面两个字节当作另外一个字符解码,还是前两个字节当作一个字符解码最后一个字节当作一个字符解码?这时候我们就遇到问题了,怎么办? 想一想这个问题的关键点是在哪里? 没有一种机制告诉我们一个字节是应该按照一个字符解码还是连续的两个字节按照一个字符解码。utf8 utf16 utf32就是干这件事情的,它是怎么做的。
还是上面那个飞的例子,飞的字符编码应该是1110xxxx 10xxxxxx 10xxxxxx,看它最左边那个字节的有三个连续的1了吗?那就是告诉解码程序,我这个字的字符编码共有三个字节,如果最左边的字节有连续的两个1就表示这个字符是用两个字节来编码的。就是这么简单。
为什么GBK GB2312字符集的码点就能直接作为字符编码?
查询GBK编码表可以知道它的字符编码是在0x8140~0xFEA0这个范围之内,看最高位的8到F
8到F这个范围中会遇到8 9 A B C D E F这几个数,把这几个数转换位二进制发现它们最高位的二进制位都是1,GBK编码是除了ASCII码之外所有的字符都用两个字节编码,GBK编码是兼容ASCII码的,ASCII码的最高位一定是0,所以我们只需要检测字节的最高位,如果是1,那么就意味着这个字符共占用两个字节,如果是0,那么这个字符就占用一个字节。根本不会出现不知道怎么解码的问题。因为GBK GB2312的字符集已经把这个怎么解码的问题解决了,就不需要再折腾字符编码那个步骤了。
字库中存储着什么信息
我猜测字库中存储着是字符编码与字形码的对应关系,可是GBK和utf8字符编码的结果是完全不一样的,难道在字库中要保存两套字符编码与字形码的对应关系吗? 还是说存在着一个转化关系。