Variable length unicode
于是,现如今的unicode采用的是可变长度编码方案。而所谓的“可变长度”包含了两个意思:
- “编码单位(code unit)”的长度是可变的;
- 构成同一个字符的“编码单位”组合也是可变的;
什么是code unit呢?简单来说,code unit和ASCII码的形式是非常类似的,它们是一个个具体的数值。不同的是,它可以由多种长度单位的数字构成:
第一种是用多个连续的8-bit数字表示一个unicode,这就是我们熟知的UTF-8编码。这种编码方式可以很好的对ASCII编码实现兼容。例如,人民币符号¥
的UTF-8编码是:C2 A5
;
第二种是用一个16-bit数字表示一个unicode,这种编码方式叫做UTF-16。例如,¥
的UTF-16编码是:00A5
;
最后,当然就是UTF-32编码,¥
的UTF-32编码是:000000A5
,你应该不难理解它的含义;
Unicode scalar?
当然,无论我们用什么编码来表示字符,和ASCII编码一样,每一个可变长unicode字符最终也会对应一个数字来表示它的编码值,这个值叫做code point。现如今,这个值的的范围是[0, 0x10FFFF],而我们只用了还不到12%的空间。因此,还有大量的空间允许我们添加诸如emoji这样的符号。
理解Surrogate pair
看到这里,你可能会想,如果我们使用UTF-16编码,根本无法全部表示上面定义的code point啊。为了解决这个问题,unicode标准保留了UTF-16编码空间中的一些值,它们永远不会被定义成字符,而是和其它UTF-16的编码值组合在一起,表示一个unicode字符。
来看一个例子:
💖 Sparkling heart的code point是1F496,显然,他不能用一个UTF-16来表示,因此,整个编码过程是这样的:
- 用code point减去0x10000,得到0x0F496;
- 把这个数字变成二进制0000 1111 0100 1001 0110;
- 取10-MSB:0000 1111 01和10-LSB:00 1001 0110;
- 用0xD800 + 10-MSB得到0xD83D;
- 用0xDC00 + 10-LSB得到0xDC96;
- 这样 D83D DC96 就是这个unicode的UTF-16编码;
而我们用到的0xD800和0xDC00,就叫做surrogate pair。
什么是unicode scalar?
理解了surrogate pair之后,就不难理解unicode scalar了。简单来说,它就是除了surrogate pair之外的code unit。在后面的视频中,我们可以看到,Swift中,我们可以使用\u{1F496}
这样的方式,来表示一个unicode scalar。
忘了String是字符数组这个事情吧
很长时间以来,在其他编程语言里,为了降低复杂性,人们总是试图隐藏一个事实:
"一个在屏幕上显示的字符可能由多个code unit组合而成。"
但这却给开发者理解unicode,甚至在处理unicode的代码上留下了很多难以发现和处理的bug。于是,在Swift里,String
并没有这样做,开发团队对这个类型最重要的一个设计目标就是尽可能保持这个类型在Unicode上的语义正确。
当然,这样做也是有代价的。在Swift里,String
已经彻底不再是一个集合类型。而是一个提供了从多个维度展现一个Unicode视图的类型。你可以得到它的多个Characters
,可以看到它的UTF-8 / UTF-16 / Unicode scalar值等等。
所以,彻底忘了String
是一个字符数组这样的事情吧。哪怕是从概念上,也不要这样去理解Swift中的String
。让自己用一个全新的方式,去理解现如今我们需要处理的字符。
网友评论