为什么Swift将这个Grapheme Cluster计为两个字符而不是一个?

Ray*_*oal 4 unicode emoji grapheme swift

一般来说,Swift 非常聪明地将字形集合计为单个字符.例如,如果我想制作黎巴嫩旗帜,我可以将两个Unicode字符组合在一起

  • U + 1F1F1区域指标符号L
  • U + 1F1E7区域指标符号字母B

正如预期的那样,这是Swift中的一个角色:

let s = "\u{1f1f1}\u{1f1e7}"
assert(s.characters.count == 1)
assert(s.utf16.count == 4)
assert(s.utf8.count == 8)
Run Code Online (Sandbox Code Playgroud)

但是,假设我想制作Fitzpatrick Type-5的自行车表情符号.如果我结合起来

  • U + 1F6B4自行车手
  • U + 1F3FE EMOJI MODIFIER FITZPATRICK TYPE-5

Swift将此组合计为两个字符!

let s = "\u{1f6b4}\u{1f3fe}"
assert(s.characters.count == 2)   // <----- WHY?
assert(s.utf16.count == 4)
assert(s.utf8.count == 8)
Run Code Online (Sandbox Code Playgroud)

为什么这两个字符而不是一个?

为了说明我期望它为1的原因,请注意该群集实际上被解释为有效的表情符号:

在此输入图像描述

nwe*_*hof 7

部分答案在emrys57的评论中提到的错误报告中给出.将Unicode字符串拆分为"字符"时,Swift显然使用了UAX#29 Unicode文本分段中定义的字形集群边界.有一个规则,不要区域指标符号之间突破,但对于表情修饰符没有这样的规定.因此,根据UAX#29,该字符串"\u{1f6b4}\u{1f3fe}"包含两个字形簇.有关解释,请在Unicode邮件列表中查看来自Ken Whistler的此消息:

这是因为修饰符的回退行为仅仅是独立的象形文字,即颜色样本图像.[...]您需要有关这些序列的其他特定知识 - 它不仅仅是对于字形集群的UAX#29规则的 默认实现.