Unicode与UTF-8

2019-03-23  本文已影响0人  E_1fe7

引言

本文章对结合以下文献对unicode进行深入理解、感谢前辈们的无私奉献!其中特别鸣谢Quinn Note:Unicode的设计和原理这篇文章作者。

1.Quinn Note:Unicode的设计和原理
2.unicode 联盟:unicode12.0文档
3.鹿文鹏、薛若娟:Unicode与UTF-8编码转换方法研究
4.阮一峰:字符编码笔记:ASCII,Unicode 和 UTF-8
5.lzjun:阮一峰的文章有哪些常见性错误


Unicode

Unicode背景就不再阐述了,前面几篇文章都有详细介绍。说白了,Unicode就是把所有符号(各国文字、数学符号、emoji表情等等)统一(标准化)起来,用二进制方式表现,每一个符号对应一个二进制值。

1.码点(code point)

Unicode中码点就是每个符号对应二进制值

中文"好"的码点是十六进制的597D。Unicode表示:U+597D
码点U+0041表示大写拉丁字母A


2.平面映射(plane)

以前Unicode是最大16bit,能表示65536字符。慢慢的各国文字的加入(光我们国家的汉字就好几万),16位完全不够用,因此现在Unicode最大到U+10FFFF(而二进制是10000 11111111 11111111,一共花费了21bit)。也就是说Unicode的值在U+000000到U+10FFFF内。当然这么多符号肯定一些很常用(英文)而一些基本极少使用,所以将Unicode的编码空间划分为17个平面(plane),每个平面包含2^16(65,536)个码位。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0x00到0x10,共计17个平面。

BMP平面内,从U+D800到U+DFFF之间的码位区块是永久保留不映射到Unicode字符,也就是说这块区域不用来表示符号,先空着。UTF-16就利用保留下来的0xD800到0xDFFF区段的码位来对辅助平面的字符的码位进行编码,后面介绍UTF-16编码原理时会细讲。


3.如何存储Unicode

有人说,存储还不简单,直接把Unicode的值存储在计算机不就好了。我负责人的告诉你,当然可以。我们来看下面例子


4.UTF-8

UTF-8的编码规则可以概括为二条:

  1. 单字节的字符,字节的第一位设为0,后面7位为这个符号的Unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
  2. 对于n字节(n = 2,3,4)的字符,第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
    所以,UTF-8的字符编码区间如下表格,x位是有效的二进制位,它们拼接起来就是对应的Unicode,表中的1和0,只是用于描述当前字符占用几个字节。
    UTF-8编码规则

5.UTF-16

  1. 从U+0000至U+D7FF以及从U+E000至U+FFFF的码位
    这个是BMP平面去掉代理区域U+D800到U+DFFF剩下的区域,UTF-16使用两个字节编码这个范围内的码位,就是其码点就是其UTF-16值。

  2. 从U+10000到U+10FFFF的码位
    这个区域是要用4个字节表示,但是他没有像UTF-8一样使用了标志位,所以我们就无法知道字符之间的边界了,不知道哪里是使用2个字节表示一个字符,哪里是使用4个字节表示一个字符。所以这里就用到前面在BMP(基本面)的代理区。

编码方法:详情查看

辅助平面中的码位,在UTF-16中被编码为32bit,4个字节。先看一下其编码算法,然后再看一个例子。辅助平面的码位(U+10000到U+10FFFF)减去0x10000,得到的值的范围U+0到U+FFFFF,最多占20bit长,我们将20bit长分为两部分,高位的10bit的值(值的范围为0到0x3FF)被加上0xD800得到第一个码元,称作高位代理(highsurrogate),值的范围是0xD800到0xDBFF。低位的10bit的值(值的范围是0到0x3FF)被加上0xDC00得到第二个码元,称作低位代(low surrogate),值的范围是0xDC00到0xDFFF。将上面获得的高位代理和低位代理结合起来得到的四个字节,就是最终的Unicode编码,我们来举一个例子。
例如U+10440编码(𐑀):
0x10440减去0x10000,结果为0x00440,二进制为0000 0000 0100 0100 0000。分区它的上10位值和下10位值(使用二进制):0000000001 and 000100000。添加0xD800到上值,以形成高位:0xD800 + 0x0001 = 0xD801。添加0xDC00到下值,以形成低位:0xDC00 + 0x0040 = 0xDC40。所以U+10440的UTF-16编码是0xD801 0xDC40。


6.总结

Unicode是字符集,如今最大能表示的字符是2的21次方(除了代理区,稍微少点),他的编码方式一般是UTF-8、UTF-16、UTF-32。最常用的是UTF-8。但是像JAVA中用的是UCS-2的方式,种淘汰的UTF-16编码。

Tips:为什么java里不推荐使用char类型呢?其实,1个java的char字符并不完全等于一个unicode的字符。char采用的UCS-2编码,是一种淘汰的UTF-16编码,编码方式最多有65536种,远远少于当今Unicode拥有11万字符的需求。java只好对后来新增的Unicode字符用2个char拼出1个Unicode字符。导致String中char的数量不等于unicode字符的数量。然而,大家都知道,char在Oracle中,是固定宽度的字符串类型(即所谓的定长字符串类型),长度不够的就会自动使用空格补全。因此,在一些特殊的查询中,就会导致一些问题,而且这种问题还是很隐蔽的,很难被开发人员发现。一旦发现问题的所在,就意味着数据结构需要变更,可想而知,这是多么大的灾难啊。

上一篇下一篇

猜你喜欢

热点阅读