字符集

字符集:
我们在计算机屏幕上看到的是实体化的文字,而在计算机存储介质中存放的实际是二进制的比特流。那么在这两者之间的转换规则就需要一个统一的标准,简单的说字符集就规定了某个文字对应的二进制数字存放方式(编码)和某串二进制数值代表了哪个文字(解码)的转换关系。字符集标准有很多,如ASCII,UTF-8(UNICODE),UTF-16(UNICODE),GBK(ASCII的中文扩展)等

字符编码:
对于一个字符集来说要正确编码转码一个字符需要三个关键元素:字库表(character repertoire)、编码字符集(coded character set)、字符编码(character encoding form)。

  • 字库表是一个相当于所有可读或者可显示字符的数据库,字库表决定了整个字符集能够展现表示的所有字符的范围。
  • 编码字符集,即用一个编码值code point来表示一个字符在字库中的位置。
  • 字符编码,编码字符集和实际存储数值之间的转换关系。
    例子:在ASCII中A在表中排第65位,而编码后A的数值是0100 0001也即十进制的65的二进制转换结果。

疑问:那既然字库表中的每一个字符都有一个自己的序号(位置),直接把序号作为存储内容就好了。为什么还要通过字符编码把序号转换成另外一种存储格式呢?因为统一字库表的目的是为了能够涵盖世界上所有的字符,但如果把每个字符都用字库表中的序号来存储的话,每个字符就需要3个字节(这里以Unicode字库为例),这样对于原本用仅占一个字符的ASCII编码的英语地区国家显然是一个额外成本(存储体积是原来的三倍)。同样一块硬盘,用ASCII可以存1500篇文章,而用3字节Unicode序号存储只能存500篇。于是就出现了UTF-8这样的变长编码。在UTF-8编码中原本只需要一个字节的ASCII字符,仍然只占一个字节。而像中文及日语这样的复杂字符就需要2个到3个字节来存储。

UTF-8和Unicode的关系
Unicode就是上文中提到的编码字符集,而UTF-8就是字符编码,即Unicode规则字库的一种实现形式。随着互联网的发展,对同一字库集的要求愈加迫切,Unicode标准也就随之出现。它几乎涵盖了各国语言可能出现的符号和文字,并将为他们编号。Unicode的编号从0000开始一直到10FFFF共分为16个Plane,每个Plane中有65536个字符。而UTF-8则只实现了第一个Plane,可见UTF-8虽然是一个当今接受度最广的字符编码,但是它并没有涵盖整个Unicode的字库,这也造成了它在某些场景下对于特殊字符的处理困难。

UTF-8编码为(可)变长编码。最小编码单位(code unit)为一个字节。一个字节的前1-3个bit为描述性部分,后面为实际序号部分。

  • 1字节=8位(1 byte = 8bit) ,1 KB = 1024 Bytes,1 MB = 1024 KB,1 GB = 1024 MB。
  • 如果一个字节的第一位为0,那么代表当前字符为单字节字符,占用一个字节的空间。0之后的所有部分(7个bit)代表在Unicode中的序号,与 ASCII 的编码是一样的。所以说 ASCII 是 UTF-8 的一个子集。
  • 如果一个字节以110开头,那么代表当前字符为双字节字符,占用2个字节的空间。第二个字节以10开头。第一个字节110之后的所有部分(5个bit)加上第二个个字节的10之后的部分(6个bit)代表在Unicode中的序号。
  • 如果一个字节以1110开头,那么代表当前字符为三字节字符,占用3个字节的空间。第二、第三个字节以10开头。第一个字节110之后的所有部分(5个bit)加上后两个字节10之后的部分(12个bit)代表在Unicode中的序号。
  • 如果一个字节以10开头,那么代表当前字节为多字节字符的第二个字节。

从一个字节到三个字节的UTF-8编码例子:


字符集_第1张图片

规律:

  • 3个字节的UTF-8十六进制编码一定是以E开头的
  • 2个字节的UTF-8十六进制编码一定是以C或D开头的
  • 1个字节的UTF-8十六进制编码一定是以比8小的数字开头的

乱码:
乱码的出现是因为:编码和解码时用了不同或者不兼容的字符集。一个用UTF-8编码后的字符,用GBK去解码。由于两个字符集的字库表不一样,同一个汉字在两个字库的位置也不同,最终就会出现乱码。

处理Emoji:
所谓Emoji就是一种在Unicode位于\u1F601-\u1F64F区段的字符。这个显然超过了目前常用的UTF-8字符集的编码范围\u0000-\uFFFF。最常见的问题就在于将他存入MySQL数据库的时候。一般来说MySQL数据库的默认字符集都会配置成UTF-8(三字节),而utf8mb4在5.5以后才被支持,也很少会有DBA主动将系统默认字符集改成utf8mb4。当我们把一个需要4字节UTF-8编码才能表示的字符存入数据库的时候就会报错:ERROR 1366: Incorrect string value: '\xF0\x9D\x8C\x86' for column。我们试图将一串Bytes插入到一列中,而这串Bytes的第一个字节是\xF0意味着这是一个四字节的UTF-8编码。但是当MySQL表和列字符集配置为UTF-8的时候是无法存储这样的字符的,所以报了错。
有两种解决方式:

  • 升级MySQL到5.6或更高版本,并且将表字符集切换至utf8mb4。
  • 第二种方法就是在把内容存入到数据库之前做一次过滤,将Emoji字符替换成一段特殊的文字编码,然后再存入数据库中。之后从数据库获取或者前端展示时再将这段特殊文字编码转换成Emoji显示。

要点:
UTF-8是一种可变长得unicode编码。


参考:
http://cenalulu.github.io/linux/character-encoding/
http://pycoders-weekly-chinese.readthedocs.io/en/latest/issue5/unipain.html

你可能感兴趣的:(字符集)