诺诺是我侄女

编码详解

 

小编最近经常被字符编码的事情搞得头都大了,翻阅了各种文档,最后终于理解了,毫无保留分享一下.

1 概念

首先需要搞明白一件事情,什么是字符,字符集,字符编码,搞清楚之后那么理解起来就会非常的顺利了.

字符;说白了就是符号,比如,”你”,”y”,”?”,就是你能在计算机能看到的符号,图片和视频不算符号.数据结构不一样

字符集:是 字符 集合,说白了就是一群符号.比如GBK,UNICODE等,如果某一个符号不在这个字符集中,打死计算机都表示不出来.就像给你生僻字,”嚋”,你的脑袋里没有影响,你就不认得了.计算机也是同理的.字符集决定计算机能表达什么东西

编码:计算机的全部数据都是二进制.怎么用二进制表示上面的这些符号,(最小的存储单位是byte,8位二进制),就是编码要做的事情.

2 ASCII码

ascii码是计算机刚开始出现的时候使用的字符集.只能表示英文字符,标点符号,控制字符.
由于需要表示的字符不是很多采用一个byte就足以.第一个bit其实没有利用起来,规定都为0,其他低7位才是真正用来表示编码,2^7=128,总共表示128个字符,每个字符对应一个二进制值,这就是它的编码方式,非常简单

  0xxx xxxx
 
  例如:
  0101 0101
  表示U

单字节字符串使用一个字节存放一个字符(SBCS)。比如,”Bob123″ 在内存中为:(16进制)

  42  6F  62  31  32 33  00
 
  B   o   b   1   2   3  \0

此时计算机是表示不来中文的.说白了,老外就没有打算给我们用.随着计算机在全球各个国家越来越流行,很多语言都想在计算机上被显示出来,ascii已经满足不来需求了

3 ANSI码

ansi码被发明了,用来解决ascii码解决不了的问题.一般的设计方法采用2个byte的编码方式来表示编码方式,因为要表示更多字符,所以一个byte 8位根本不够用,全部用了2^8=256个字符,对我们天朝而言,简直就是笑话.2^16=65536,这个才能基本覆盖天朝的日常用字.注意ansi码不是具体的编码方式,是一类相似的编码方式的统称,其中比较有代表性的就是GB2312,BIG5,JIS.都是使用两个字节存储编码
其中0-127(00–7F)采用和ascii相同处理方式,一个byte就够了,超出127(0111 1111)范围的采用2个byte表示(1000 0000 0000 0000),中间加空格是为了方便你阅读,没有别的意思.2^15 = 32768个除ascii码外可以表示的字符数.(1000 0000 0000 0000-1111 1111 1111 1111)但现实是没有全部利用上.
每个字符使用一个字节或多个字节来表示(MBCS),因此,这种方式存放的字符也被称作多字节字符。比如,”中文123″ 在中文 Windows 95 内存中为7个字节,每个汉字占2个字节,每个英文和数字字符占1个字节:

  D6    D0    CE    C4    31    32    33    00
 
  中    文    1    2    3    \0

不过随着国际间交流越来越多,很多时候一份文档中既有中文又有日文.这下子麻烦了,如果编码方式改成gbk2312的话,日文就全部乱码,编码方式改成日文的JIS,那么中文的就全部乱码了,你说蛋疼不蛋疼?

4 UNICODE码

这个时候UNICODE被发明了出来,再也不会出现ansi码的蛋疼事情了.

计算机存放字符串时,改为存放每个字符在 UNICODE 字符集中的序号。目前计算机一般使用 2 个字节(16 位)来存放一个序号(DBCS),因此,这种方式存放的字符也被称作宽字节字符。比如,字符串 “中文123” 在 Windows 2000 下,内存中实际存放的是 5 个序号:

  2D    4E    87    65    31    00    32    00    33    00    00    00
  中           文           1           2           3         \0     
  一共占 10 个字节。

unicode采用16位来存放一个字符,号称能存下地球上的全部字符.有人就说不可能
2^16=65536个字符,怎么可能存下地球上的全部字符?就我大天朝全部的字都够你喝一壶的了.其实是你不了解unicode的编码特性.unicode表示的字符数是有限制的,但是并不是65536,而是000000-10ffff个(2^16)*17 =1114112个,这个我相信已经非常多了,存下我天朝的字还是没有压力的,据说unicode要把甲骨文编码进来,我捻了一把汗,会不会有些人拿他来装B?
谈unicode之前先搞清楚,Unicode和utf-8 ,utf-16等的关系,对理解上面问题有非常好的帮助.Unicode是字符集,也就是说unicode负责收入地球上的各种符号,每个符号给一个唯一的unsigned int类型的数字而已,并不关心我用2个字节还是用4个字节来编码.编码的事情由utf来负责(unicode transformation format).utf-8,utf-7,utf-16就是负责将具体的unsigned int类型的数字用二进制表示在计算机中的.所以为什么unicode采用16位来存放一个字符能存下地球上的全部字符,其实只是说对了一半,0号平面就是16位bit表示的整数来表示BMP(Basic Multilingual Plane,即基本多文种平面,包含我们最经常使用的符号,多国的经常使用的字符和符号,其他不适经常使用的没有放到0号平面中),当然一般使用都是没有问题.但是其他经常不使用的字符放到2-17号平面中,其中的需要的bit数就不止16位了,最多需要20位,具体参考下面的Unicode字符平面映射.如果不懂得话,这里已经写得非常好了,请看wiki请参考Unicode字符平面映射,utf8

5 UTF编码:utf8和utf16

utf8与utf16的不同之处之一就是utf8兼容ascii编码,而utf-16不具备向后兼容的特性.
utf8每个范围采用不同字节数,

十六进制 UTF-8 二进制/十六进制 注释
000000 – 00007F 128个代码 0zzzzzzz(00-7F ASCII字符范围,字节由零开始
000080 – 0007FF 1920个代码 110zzzzz(C0-DF) 10zzzzzz(80-BF) 第一个字节由110开始,接着的字节由10开始
000800 – 00D7FF 00E000 – 00FFFF 61440个代码 1110zzzz(E0-EF) 10zzzzzz 10zzzzzz 第一个字节由1110开始,接着的字节由10开始
010000 – 10FFFF 1048576个代码 11110zzz(F0-F7) 10zzzzzz 10zzzzzz 10zzzzzz 将由11110开始,接着的字节由10开始

这么看你可能不是很明白,

单字节: 1xxxxxxx

2个字节: 110xxxxx 10xxxxxx

3个字节: 1110xxxx 10xxxxxx 10xxxxxx

4个字节: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

发现规律了吧,底层实现通过第一个字节的1的个数来判断后面几个字节是否是属于同一个字符,这种设计的专业术语叫做”自同步”(self synchronization),但是对比utf-16就会发现,utf-8兼容ascii码和节省常用二进制位数做的非常好,但是对于除ascii编码外的字符,要使用2-4个字节,而utf-16一般是2个字节,少数生僻的需要4个字节,所以到底采用utf-8还是utf-16,取决于你的文本内容的到底是英文或常用字多还是生僻字符多,如果是生僻字符多,选择utf-16可能更节省空间一些.一般情况下建议utf-8编码

utf-16通常采用16位或2个16位来表示一个字符,一个16位bit取了个专业术语叫”码元”,这里顺便说一下,ucs-2编码是utf-16的子集,ucs-2采用一个16位bit表示一个字符,,所以最多2^16=65536个字符,也就是0号平面的全部字符,和utf-16一模一样,但是它对1-17号平面的字符表示吃不消.ucs-2基本已经淘汰使用了.因为这个缺陷

问题来了,ucs-2不能表示的1-17号unicode映射平面,utf-16是怎么做到的?

utf-16 从U+D800到U+DFFF之间的码位区块是永久保留不映射到Unicode字符,

0xD800 1101 1000 0000 0000

0xDFFF 1101 1111 1111 1111

这就是秘密,给你举个🌰,

如果要表示U+1F416编码一头猪(🐖)怎么做呢?下面一步一步详细说明一下

字符编码详解

如果还是看不懂,请参考下面给出的wiki连接

5 讨论一下emoji表情为什么用utf8mb4存储,不用utf8在mysql中

在MySQL中,utf8采用的是3个byte来存储字符串,因为绝大多数的字符都可以得到很好的满足,也是就unicode中的0号平面(基本多文种平面,BMP)
但是emoji表情属于1号平面的字符,所以MySQL在存储的时候会报错或者装换成???,要想正常的保存必须使用utf8mb4,采用4个字节存储.
除了这个之外顺便说一下创建mysql连接的时候,需要指定charset=utf8mb4,否则是没有用的哦,表结构或者某个字段的字符编码也需要指定为utf8mb4,
例如:”
name varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci

文档信息

版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)

本文出自 “richardtumas”的博客,请务必保留此

发表日期: 2017年7月21日

邮件: joy@richardtumas.com

参考文档:

[1]unicode emoji表情

[2]unicode 平面映射 wiki

[3]utf-16 wiki

[4]utf-8 wiki

[5]utf8 difference between utf8 and utf8mb4

[6]编码推荐阅读

[7]ascii wiki