Unicode与UTF-8

引言

本文章对结合以下文献对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个平面。

  • 第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0),码点范围从U+000000----U+00FFFF。
  • 其他平面称为辅助平面(Supplementary Planes),码点范围从U+010000一直到U+10FFFF。
  • 为什么要分区?这块后面说到存储方式就明白了,先记着

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


3.如何存储Unicode

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

  • 'A'的码点是U+65
    因为Unicode最大是21位,至少需要4个字节表示全部信息,因此'A'的存储不是直接用一个字节表示01100101存储,而是四个字节:00000000 00000000 00000000 01100101这样。这样的存储方式就叫做UTF-32,每个Unicode码点都用四个字节表示。哇,真的是浪费空间。为了解决这一问题,变长字节编码方式来了——UTF-8

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编码规则
  • 例子
    Tips:计算机中计量基本单位是字节,即8bit
    'A'的码点是U+65(01100101)
    '中'的码点是U+4E2D(01001110 00101101)
    1. 通过UTF-32存储
      "A中":00000000 00000000 00000000 01100101 00000000 00000000 01001110 00101101存储在计算机硬盘上。
      我才不管你多长,反正所有信息我都用4个字节表示。当计算机读取文件信息时,他会以每4个字节为单位读取信息,然后用来在Unicode字符集里面查询对应的码点,转换成符号。虽然很浪费空间,但是方便啊,Unicode对应字符的码点和存储的值是一样大小的,多方便。

    2. 通过UTF-8存储
      根据上面的对应表。
      'A'在U+0000——U+007F区间内,所以存储值是:01100101 。发现没有,其实就是他的码点值,没变,其实也是ASCII值。
      '中'在U+0800——U+FFFF区间内,所以存储值是:11100100 10111000 10101101。到这里可以发现,对于码点是1个字节可以表示的,那么其UTF-8编码也是1个字节搞定(U+80——U+FF除外),其他n(2,3,4)字节才能表示的码点值在UTF-8中需要n+1字节表示,是不是比UTF-32省空间多了。但是其编码多了写标志信息,如在'中'的UTF-8编码中,使用1110表示我这个码点是需要3个字节编码,后面的每个字节都以10开头,表示这些字节的信息属于上面那个1110头。是不是要麻烦点,但是没关系,这些规则是为了让计算机能正确从UTF-8中读取到正确的Unicode码点值,以获得相应符号。所以累的是计算机,但对于计算机这都不是事。
      "A中":01100101 11100100 10111000 10101101存储在计算机硬盘上。在解码的时候,计算机会根据字节前面的标志位是0110111011110来判断改码点是由几个字节组成,然后把相应后面字节信息转化成码点得到相应符号。这就是变长方式的编码,所以现在最常用的存储方式是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中,是固定宽度的字符串类型(即所谓的定长字符串类型),长度不够的就会自动使用空格补全。因此,在一些特殊的查询中,就会导致一些问题,而且这种问题还是很隐蔽的,很难被开发人员发现。一旦发现问题的所在,就意味着数据结构需要变更,可想而知,这是多么大的灾难啊。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352