Unicode和UTF-8、UTF-16、UTF-32

写在前面

如果你是iOS开发者,并且在处理NSString字符上遇到了一些问题,强烈建议去看看Objc中国上关于 NSString 与 Unicode

简介

Unicode对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。这是维基百科对Unicode下的定义。
Unicode的实现方式包含了UTF-8、UTF-16(字符用两个字节或者四个字节表示)和UTF-32(用四个字节来表示),下面对面一一进行介绍。

UTF-8

UTF-8的最明显的一个特点是它是变长的,它可以使用1到4个字节表示一个符号,根据不同的符号变化字节长度
先把阮一峰在《字符编码笔记:ASCII,Unicode和UTF-8》中对UTF-8的编写规则的一个总结放出来。

⚠️⚠️⚠️UTF-8的编码规则很简单,只有二条:
1、对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2、对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

提出问题:第一个字节前面n位设1是为了知道当前字符占用多少字节,而后面字节的前两位字节为什么要设置为10呢?下面会马上进行解释。
现在我们来具体的分析一下Unicode的不同范围:下面中前两个描述是否为ASCII,后两个描述多字节序列

  • U+0000到U+007F(ASCII)
    从U+0000到U+007F被编码为0x00~0x7F的单字节,这是ASCII码的所有字符,一共128个字符,所以Unicode是完全用来容纳ASCII的。

    回答上面提出的问题:后面字节的前两位一律设为10(10000000也就是80)是因为必须要大于7F才和ASCII码分开。

  • 大于 U+007F(非 ASCII)
    所有大于 U+007F 的字符被编码为一串多字节序列,这样就可以区分一串多字节序列是多字节码还是 ASCII 码。

  • 0xFE 和 0xFF 不会被用于 UTF-8 编码中。

  • 多字节序列的第一个字节在0xC00xFD中,剩余字节在0x800xBF内。
    这里解释一下为什么第一个是在0xC0~0xFD中,理解这里需要再回去看看上面注意中提到的Unicode编码规则。因为表示的是多字节就表明n是大于1的,所以第一个字节最小的值为:11000000即C0(表明当前有两个字节。每四位表示一个十六进制数,这也是为什么在编程的时候喜欢用十六进制数的原因),如果在没有限制的情况下,通过上面的结论我们可以得到第一个字节能表示的最大的数是0xFE(11111110),就是前面7位设置1最后一位设置为0,但是上面一条中提到不包含FE,所以第一个字节的最大值为0xFD(11111101)
    同理因为后面字节的前两位一律设为10所以多字节除了第一个字节的其他字节最大值为10111111(BF)

总结
UTF-8 编码字符最长可达六个字节

Unicode 字符:             UTF-8 码:
U-00000000 - U-0000007F:    0xxxxxxx      ///表示ASCII
U-00000080 - U-000007FF:    110xxxxx 10xxxxxx      ///
U-00000800 - U-0000FFFF:    1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF:    11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF:    111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF:    1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

举个例子:汉字“王”的Unicode码为U+73B8转换为二进制为:0111 0011 1010 1000,"73b8"位于上面的第三类,把转换的16个二进制依次放入(一定是一次放入不管空格的)上诉的x中:

11100111 10001110 10101000
/// 同样我们来验证一下阮一峰举例的“严”字
/// 4E25 同样属于上诉的第三类 ,对应的二进制 => 0100 1110 0010 0101
/// 11100100 10111000 10100101 得到的结果和他文章中的一样。

到这里我自己觉得应该是把UTF-8的编码方式说清楚了,最后再来一个编码的顺序(很适合于我的方式)

编码的顺序
对于单字节:
直接将其转换为八位的二进制就可以了;
对于多字节:

  • 1.找到Unicode码对应的二进制数据
  • 2.查看该Unicode码在分类中属于第几类
  • 3.一次填入二进制码

UTF-16

UTF-16Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为"storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。
这里需要说明一下基本多文种平面-BMP辅助平面-SMP,在维基百科中每一个平面相关的图片下面都说了"每个写着数字的格子代表256个码点",即00~FF。例如:位于BMP中00格子中的一个码点表示为:0x00E5
下面同样用一下阮一峰的规则总结:

基本平面的字符占用2个字节,辅助平面的字符占用4个字节。也就是说,UTF-16的编码长度要么是2个字节(U+0000到U+FFFF),要么是4个字节(U+010000到U+10FFFF)。

为了能够区分它本身是一个字符,还是需要跟其他两个字节放在一起解读。在BMP中,从U+D800U+DFFF之间BMP的区段是永久保留不映射到字符(从维基百科的图中D8~DF之间表示unallocated code points)。

UTF-16结论(D800~DFFF)

具体来说,辅助平面的字符位共有pow(2,20)个,也就是说,对应这些字符至少需要20个二进制位。UTF-16将这20位拆成两半,前10位映射在U+D800到U+DBFF(空间大小pow(2,10)),称为高位(H),后10位映射在U+DC00到U+DFFF(空间大小pow(2,10)),称为低位(L)。这意味着,一个辅助平面的字符,被拆成两个基本平面的字符表示。

HHHH HHHH HHLL LLLL LLLL

🌈🔥🔥🔥注意-结论
高位:D800~DBFF;
低位:DC00~DFFF;

所以,当我们遇到两个字节,发现它的码点在U+D800到U+DBFF之间,就可以断定,紧跟在后面的两个字节的码点,应该在U+DC00到U+DFFF之间,这四个字节必须放在一起解读。(而不会拆成两个字节来读)

解释一下这里为什么是pow(2,20),在基本平面之外有16个辅助平面(即pow(2,4)),而每一个辅助平面pow(2,16)个码位(辅助平面和基本平面一样,每个码位里面都包含了256个码点)。

///辅助平面字符,转码公式。
/// js代码
H = Math.floor((c-0x10000) / 0x400)+0xD800
L = (c - 0x10000) % 0x400 + 0xDC00

H为上文提到的高位,L位上文提到的低位。
举例说明一下:

/// 对于小于0xFFFF的即基本平面的字符,为两个字节
U+8D9E = 0x8D9E  ///对应的二进制格式为:10001101 10011110

/// 对出于辅助平面的字符
/// 对于U+1D306
H = Math.floor((0x1D306-0x10000) / 0x400)+0xD800 = d834
L = (0x1D306 - 0x10000) % 0x400 + 0xDC00 = df06

UTF-32

因为UTF-32对每个字符都使用4字节,就空间而言,是非常没有效率的。特别地,非基本多文种平面的字符在大部分文件中通常很罕见,以致于它们通常被认为不存在占用空间大小的讨论,使得UTF-32通常会是其它编码的二到四倍。

结论

所以当我们在使用字符串的时候,通常使用length的时候,要看他的编码方式,并不是一个字符就代表了一个字节有可能是两个字节、四个字节甚至可能最多能到6个字节都是有可能的,这应该就能理解Swift中对于字符串的处理了。

参考文献

Objc中国上关于 NSString 与 Unicode
Unicode代码图表
字符编码笔记:ASCII,Unicode和UTF-8
什么是UTF-8
Unicode与JavaScript详解
Unicode编码及其实现:UTF-16、UTF-8,and more
UTF-16
UTF-32

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

推荐阅读更多精彩内容