【问题标题】:Swift countElements() return incorrect value when count flag emoji计数标志表情符号时,Swift countElements()返回不正确的值
【发布时间】:2015-01-07 20:20:46
【问题描述】:
let str1 = "????????????????????????????????????????"
let str2 = "????????.????????.????????.????????.????????."

println("\(countElements(str1)), \(countElements(str2))")

结果:1​​、10

但是str1不应该有5个元素吗?

这个错误似乎只在我使用标志表情符号时出现。

【问题讨论】:

  • 是的,这很奇怪。我尝试了不同的表情符号,只有标记表情符号会导致这种情况。即使使用没有空格的不同标志也会导致这种情况。
  • 这对我来说似乎是一个错误。 “??????”是由区域指示符符号字母 D 和区域指示符符号字母 E 组合而成的“扩展字素簇”,并计为单个字符,但“????????????????????? ??????????????????”应该是 5 个字符。甚至 ”???????????????????????????????????????????????? ?????????”给出一个字符数。也许对 Unicode 标准比较了解的人可以解释一下。
  • 似乎任意序列的“区域指示符号字母”被视为单个字素簇。例如,let str1 = "\u{1F1E6}\u{1F1E7}\u{1F1E8}\u{1F1E9}\u{1F1EA}\u{1F1EB}" 打印为 ???????????????????????? 但算作单个字符。
  • 有趣的是,str1.startIndex.successor() == str1.endIndex

标签: ios swift unicode emoji countelements


【解决方案1】:

Swift 4 (Xcode 9) 更新

根据 Unicode 9 的要求,从 Swift 4(使用 Xcode 9 beta 测试)开始,字素簇在每两个区域指示符符号后中断 标准:

let str1 = "??????????"
print(str1.count) // 5
print(Array(str1)) // ["??", "??", "??", "??", "??"]

另外String 是其字符的集合(再次),所以可以 使用str1.count获取字符数。


(Swift 3 及更早版本的旧答案:)

来自"3 Grapheme Cluster Boundaries" 在“标准附件#29 UNICODE TEXT SEGMENTATION”中: (强调):

一个遗留字素簇被定义为一个基(例如 A 或 カ) 后跟零个或多个连续字符。一种思考方式 这是形成“堆栈”的一系列字符。

base 可以是单个字符,也可以是 Hangul Jamo 的任何序列 组成韩文音节的字符,由 D133 在 The Unicode 标准,或者是Regional_Indicator (RI) 字符的任何序列。 RI 字符成对使用来表示 Emoji 对应于 ISO 国家代码的国旗符号。序列 两个以上的 RI 字符应由其他字符分隔, 比如U+200B ZWSP。

(感谢@rintaro 提供链接)。

一个 Swift Character 代表一个扩展的字素簇,所以它是(根据 参考)更正区域指示符号的任何序列 被计为一个字符。

您可以用零宽度非连接符分隔“标志”:

let str1 = "??\u{200C}??"
print(str1.characters.count) // 2

或插入一个零宽度空间:

let str2 = "??\u{200B}??"
print(str2.characters.count) // 3

这也解决了可能的歧义,例如应该“?​?​?​?” 是“?​??​?”还是“??​??”?

另请参阅How to know if two emojis will be displayed as one emoji? 了解可能的方法 计算 Swift 字符串中“组合字符”的数量, 这将为您的let str1 = "??????????" 返回5

【讨论】:

  • 不错的收获!现在知道为什么他们会这样设计它会很有趣,恕我直言,这是一个疣。
  • @rintaro:这是另一个例子:let a = "J\u{1F1EF}"; let b = "\u{1F1F5}P"。然后3 = countElements(a+b) < countElements(a) + countElements(b) = 2 + 2。 – 我的解释:c = a + b 连接字符串的 Unicode 标量值,而不是字符。因此c 可能有ab 中不存在的字素簇。
  • Randy,这不是 Swift 的错,因为它正确地实现了 Unicode 标准。 Unicode 联盟面临的问题是如何确定区域指示符之间的中断。有三个基本选项: 寻找第一个指标并每两个计数一次(可能很慢);将两个指标与一个不可见的字符粘合在一起;或拆分具有不可见字符的指标对。他们最终选择了选项 3。
  • @sudo 请注意标准规定“应该用其他字符分隔”。换句话说,没有人通过省略这些分隔符来打破标准。在大多数情况下,无论如何都没有必要这样做。然而,它确实有助于消除歧义。我没有时间对此进行调查,但我似乎记得 Cocoa 文本系统有自己的逻辑来确定字符边界,即。 e.与 NSString 分开(或除此之外),当然也与 Swift String 分开。
  • 值得注意的是,从 Unicode Standard Annex #29 的 Unicode 9.0.0 和 version 29 开始,规则发生了变化。在一系列区域指标符号中,字素簇在每两个区域指标符号之后中断。不知道 Swift 有没有执行新规则。
【解决方案2】:

对于 Swift 3,我是这样解决这个问题的:

let str = "??????????" //or whatever the string of emojis is
let range = str.startIndex..<str.endIndex
var length = 0
str.enumerateSubstrings(in: range, options: NSString.EnumerationOptions.byComposedCharacterSequences) { (substring, substringRange, enclosingRange, stop) -> () in
        length = length + 1
    }
print("Character Count: \(length)")

这解决了字符数和表情符号的所有问题,是我找到的最简单的方法。

【讨论】:

猜你喜欢
  • 2019-01-10
  • 1970-01-01
  • 1970-01-01
  • 2018-10-21
  • 1970-01-01
  • 1970-01-01
  • 2019-01-20
相关资源
最近更新 更多