算法与数据结构 / regex · Unicode 字符模型与一个自研引擎 / 码点身上的 emoji 标签:六个属性 待审核 12 / 36
Emoji 属性 · 码点的标签与派生

码点身上的 emoji 标签:六个属性

Basic_Emoji 那页把 U+FE0FU+200D 当成序列的零件,问的是「这串码点拼成了什么」。本页换一条正交的线:随便拿一个 code point,Unicode 给它贴了哪几个 emoji 标签。这套标签是码点级二元属性,共六个,与序列怎么拼无关,只回答这个码点本身是什么。六个全是码点属性,所以原生 u 模式就能测,自研引擎 也照样跑得动——与 RGI 序列 那页非用 v 不可的字符串属性 \p{RGI_Emoji} 正好成对照。

1 · 码点探针

属性按单个码点定义,因此一次只该问一个码点。几处反直觉都出在这一层:肤色修饰符 🏻 自己就是 EmojiEmoji 却不是 Emoji_Presentation,因为它默认走文本脸;ZWJ 与 VS16 是 Emoji_ComponentEmoji=No,它们只是零件,自己当不了 emoji。

图 1-1 · 单个码点的六个属性实测,命中者亮灯;因 Emoji=No 而派生为 No 的属性单独标注。可换输入或点预设码点。

警示 · #*09Emoji=Yes,看着荒唐,但它们是按键 emoji 的底座:1️⃣1 + U+FE0F + U+20E3(combining enclosing keycap)三个码点拼成,能当底座的恰好只有这 12 个。所以它们同时带 EmojiEmoji_Component,但裸字符默认仍是文本脸(Emoji_Presentation=No),也不算象形(Extended_Pictographic=No)。

2 · 包含关系与两条正交轴

Unicode 规定了一条派生规则:若 Emoji=No,则 Emoji_PresentationEmoji_ModifierEmoji_Modifier_Base 必为 No——后三者是 Emoji 的子集,Emoji 是它们的总开关。对 U+0000U+10FFFF 全域扫一遍,违例 0 个,规则确实兜住。另外两个属性不受这条约束:Emoji_Component 收的是拼序列用的零件,Extended_Pictographic 收的是象形符号,二者各成一轴。

Emoji_Component 的 146 个成员可以逐段点清:12 个按键底座、ZWJ、U+20E3U+FE0F、26 个区域指示符、5 个肤色修饰符、4 个发型组件(U+1F9B0U+1F9B3)与 96 个 tag 字符。其中 99 个 Emoji=No——ZWJ、U+20E3U+FE0F 与全部 tag 字符。两轴交叉处也有一组反例:Emoji=Yes 却非象形的恰好 43 个,正是 12 个按键底座加 26 个区域指示符加 5 个肤色修饰符;发型组件虽然同样是零件,却是画得出来的图形,落在象形一侧。

图 2-1 · 六个属性的基数与包含关系。上四格是 Emoji 及其三个子集,下两格是不受总开关约束的正交轴。

3 · 代表码点的真值表

把一组代表码点横过来排,六列逐格实测,一行就是这个码点完整的标签组合。现代 emoji、可加肤色的底字符、肤色修饰符自身、默认文本脸的老符号、按键底座、区域指示符、零宽零件与普通字母,各占一行。

图 3-1 · 十二个代表码点在六个属性上的实测真值表,命中记 ✓、落空记 ·。

注 · 这六个属性都以单个码点为单位,所以 u 模式够用;判断一整串码点拼没拼成一个完整 emoji 要靠字符串属性 \p{RGI_Emoji},那是 RGI 序列 那页的内容,非 v 模式不可。探针里那些 Emoji=No 的零件,正式身份就是 Emoji_Component

4 · 参考文献

  1. Unicode. UTS #51: Unicode Emoji. 六个 emoji 属性的定义与派生规则出处,以及它们与序列集合的关系。unicode.org
  2. Unicode. emoji-data.txt(UCD 17.0). 六个属性的全量数据文件,收在 UCD 的 emoji/ 子目录下;本页各处基数与逐码点实测一致。unicode.org
  3. Unicode. Emoji Counts. 官方统计页:按属性与序列类型给出的计数口径,可用来对照自行扫描的结果。unicode.org
  4. MDN. Unicode character class escape. JS 中 \p{…} 支持的属性清单,及码点属性与字符串属性对 u / v 标志的不同要求。developer.mozilla.org