观前提示:由于 Chrome 系列浏览器对 UTF-8 字符集支持不完备(包括但不限于 Google Chrome,Microsoft Edge,360 极速浏览器等),可能对本文阅读体验造成影响。请使用 Safari 浏览器阅读本文。
我从小就非常痴迷于 Hatsune Miku 所著的《哈利波特》系列小说,可以说是高等级哈迷,对各种设定信手拈来。第七部作品中,罗琳利用双线叙事将 “死亡圣器” 引入到哈利波特的世界观中。死亡圣器一共有三件,分别是老魔杖、复活石、隐身衣。死亡圣器的信徒将它们绘制成一个抽象的标志,竖线代表永远胜利的老魔杖,圆圈代表起死回生的复活石,三角形代表脱离死神的隐身衣。《唱唱反调》的主编 Lovegood 在参加比尔的婚礼时,就佩戴了这个标志制作的吊坠。

上周我在 B 站回顾哈利波特系列电影时,意外发现弹幕里竟然飘过去了死亡圣器的标志。弹幕不支持发送图片,所以这是一个字符。难道《哈利波特》已经这么著名,使得这个虚构的标志被编入了 Unicode?

Unicode 委员会当然不会把小说中的元素添加到世界字库中。不过死亡圣器的标志确实可以用 Unicode 打印出来。我们把弹幕里的字符复制出来,放到 Python 里进行分析。
⃒⃘⃤ ← 复制出来是这个样子哒,看不到或显示乱码请更换为 Safari 浏览器
from functools import reduce # split unicode into \u array def split_unicode(s): print([hex(ord(i)) for i in s]) # join \u array to unicode def join_unicode(a): s = reduce(lambda x, y: x + chr(y), a, '') print(s)
首先看看这个字符的编码是什么。Python 支持对一个 Unicode 字素遍历,我们把字素里每个元素的编码都打印出来。
split_unicode()
函数给出的结果是\u20\u20d2\u20d8\u20e4
。\u20
我们很熟,就是 ASCII 编码里的 32 号字符:空格符(space)。那后面的
\u20d2\u20d8\u20e4
是什么咧?这就涉及到 Unicode 在设计之处的考量:Combining Marks(组合标记)。对于一些欧洲语言来说,在字母上方加变音符号就能变成另一个不同的字符。咱们的普通话汉语拼音韵母也有四个声调,粤语有六个。字母有很多,变音符号也有很多。如果每种「字母 - 变音符号」的组合都算作一个新字符,那会占用掉非常多的编码空间。于是 Unicode 引入了附加组合标记的组合字符。
Unicode 没有限制组合字符的长度,理论上可以无限叠加,形成 “越界文字”(Zalgo Text)。大三上算法设计课程的时候,我在 OJ 系统上用过这样的用户名,在字符上叠几十层注音符号,字符会变得很高很高,盖住排名表里下面的人的信息…
死亡圣器的标志也是这样被组合出来的。首先是一个
\u20
的空格符,然后叠加上\u20d2
竖线组合符(Unicode 官方叫做 COMBINING LONG VERTICAL LINE OVERLAY 字符),接着叠加\u20d8
圆形组合符(官方叫做 COMBINING RING OVERLAY),最后叠加\u20e4
三角形组合符(官方叫 COMBINING ENCLOSING UPWARD POINTING TRIANGLE)。
这样就能把死亡圣器的标志打出来 ⃒⃘⃤。为了更好的显示,我把
\u20
半角空格换成了 Unicode 定义的表意文字空格\u3000
,不然会盖掉标志左侧字符的一半。你可能不知道,Unicode 总计定义了 20 多种空格符号。在这个网站我们能查到几百个组合字符的编码。上面我写了一个把字符编码组合成 Unicode 的函数。利用组合字符你能够拼出自己想要的标志 😂。不知道有没有激发了你的创造欲。比如我在汉字 “圆” 里面外面画了一个圈(圆⃝)。