RGI:国旗、子地区旗与肤色序列
并非所有理论上合法的码点组合都能在各平台正常显示,Unicode 为此圈出一个子集:RGI(Recommended for General Interchange),标准认可、主流平台通常都支持、可在系统之间可靠传输。Basic_Emoji 那一档是 RGI 的一部分,本页拆开它之上的三类多码点序列,最后回到日常业务真正该用的那一个属性。
1 · 国旗:两个区域指示符
U+1F1E6–U+1F1FF 这 26 个码点叫区域指示符,与字母 A–Z 一一对应。两个连写即构成一面国旗,字母对取自 ISO 3166-1 的双字母地区码。26 × 26 = 676 种组合里,\p{RGI_Emoji_Flag_Sequence} 只认 259 种;ZZ 这类未分配的组合落空,AA、DD
这些同字母对也全不成立。每个区域指示符都是补充平面字符,各占一个 UTF-16 代理对,所以一面国旗在 JS 里 .length === 4。
警示 · 序列合法与屏幕上画出旗子是两件事。\p{RGI_Emoji_Flag_Sequence} 只回答这两个区域指示符是不是一面 RGI 国旗,至于渲染成什么由字体与系统决定:中国大陆地区的 iOS 与部分 Android 不把 TW(U+1F1F9 +
U+1F1FC)渲染成旗子,只回退成两个字母方块,而正则那一侧照样判它合法。判断本机能否画出某面旗,见 emoji 渲染检测。
2 · 子地区旗:黑旗加 tag 标签
国旗只能表达国家一级的地区码,苏格兰、英格兰这类子地区另有一套办法:拿黑旗 U+1F3F4 当基底,后面跟一串 tag 字符,末尾用 U+E007F(cancel tag)收尾。tag 字符 U+E0020–U+E007E 与 ASCII 可打印字符一一对应,于是不必新增码点就能把地区码「写」在旗上。gbsct
拼出苏格兰旗,全序列共七个 code point。
这套机制看着可以无限扩展,实际收得极紧:把所有双字母地区码配上 1 到 3 位的字母数字子码穷举一遍,\p{RGI_Emoji_Tag_Sequence} 命中的只有 gbeng、gbsct、gbwls 三条,与上游
emoji-sequences.txt 里的三行完全对上。想给别的地区造一面旗,正则这一关就过不去。
3 · 肤色:底字符加修饰符
U+1F3FB–U+1F3FF 是五个肤色修饰符,接在支持修饰的底字符后面即构成一个肤色序列。底字符限于人、身体部位与人类动作一类,Emoji_Modifier_Base 共 134 个,给 😀 或 🐕 接肤色不成立,因为它们不在其中。而 \p{RGI_Emoji_Modifier_Sequence} 只收
665 条,除以 5 档肤色是 133 个底字符——比属性少一个。逐条比对后落单的是 U+1F46A(👪,一家三口):它带 Emoji_Modifier_Base=Yes,却没有任何一条 RGI 修饰序列,👪 加肤色实测既非修饰序列也不是 RGI_Emoji。属性留着、序列不收,UTS #51
未给出理由。顺着实测再看一层:家庭类整体都不收肤色,写成 👪 或写成 🧑🧑🧒 这种各人分开的 ZWJ 家庭,一旦带上肤色都不是 RGI;而同样是多人的牵手 🧑🏻🤝🧑🏻、职业类 🧑🏻🚀 却收。
修饰符在多码点序列里的位置容易看错:肤色紧跟在被修饰的那个人后面,而不是缀在整串末尾。🧑🚀 加中等肤色写作 🧑 + 肤色 + ZWJ + 🚀,实测它不属于 RGI_Emoji_Modifier_Sequence(那个集合只收「底字符 + 肤色」两码点的形态),却仍是合法的
RGI_Emoji,因为它被归到了 ZWJ 序列一类。旧版本页把这种情形报成「该基底不支持肤色修饰符」,是把两个集合的边界读错了。
4 · 整体匹配
日常业务不必逐类去认。\p{RGI_Emoji} 匹配一个完整的 RGI emoji,上面各类序列它全收:单码点、加 VS16、按键、国旗、tag 旗、肤色、ZWJ 序列。它是字符串属性,一次匹配的结果可能横跨好几个码点,因此只能用在 v 模式里,u 模式会直接报错。
数据文件那边可以把这个集合点清:emoji-sequences.txt 展开后有 Basic_Emoji 1400 条、按键 12 条、国旗 259 条、tag 旗 3 条、肤色 665 条,emoji-zwj-sequences.txt 另有 ZWJ 序列 1614 条,合计 3953。而 emoji-test.txt 里标为 fully-qualified 的是 3944 条,两者差 9,正是 5
个肤色修饰符与 4 个发型组件——它们在那份清单里被单列为 component,自己不算一个可用的 emoji。
注 · v 模式(ECMAScript 2024 的 unicodeSets)是字符串属性的前提,自研引擎 只实现到码点属性,所以 \p{Emoji_Presentation} 跑得动、\p{RGI_Emoji} 跑不动。这也是
六个 emoji 属性 那页与本页的分界。
5 · 参考文献
- Unicode. UTS #51: Unicode Emoji. RGI 的定义与四类序列的构造规则:区域指示符对、tag 序列、修饰符序列与 ZWJ 序列。unicode.org
- Unicode. emoji-sequences.txt(Emoji 17.0). 国旗 259、tag 旗 3、肤色 665、按键 12 与 Basic_Emoji 的全量条目,本页各计数照它统计。unicode.org
- Unicode. emoji-zwj-sequences.txt(Emoji 17.0). 1614 条 RGI ZWJ 序列的清单,家庭、职业与彩虹旗都在其中。unicode.org
-
TC39. RegExp
vflag with set notation + properties of strings. 字符串属性与v标志的提案仓库(Stage 4,已并入 ES2024),说明\p{RGI_Emoji}为何不能用于u模式。github.com - Unicode. Full Emoji List. 各厂商字体对同一序列的渲染对照表,可用来核对某面旗在某平台上到底画不画得出来。unicode.org