通常来讲,Swift 里的 String 是和 NSString 桥接的,比如我曾写过 NSString 和 String 究竟 有什么区别 ?,总之这里我们主要来讨论一下,String 的 count 和 NSString 的 length 到底有什么区别。
String.count
String.count 实际上是 String.Characters.count (Swift 早期版本),Swift 里的 String ,早期 count 属性就是单纯的字符数量,这和 NSString 是一样的,但很快就加入了扩展字形集群特性,比如一个 Emoji 表情,它的编码长度是普通字符的两倍,但这是视觉上的【一个】字符,于是它在 String 里算【一个】字符。
比如:
1
2
3
4
5
|
let s = “?”
print(s.count)
// result is 1
|
Note that Swift’s use of extended grapheme clusters for
Character
values means that string concatenation and modification may not always affect a string’s character count.注意 Swift 为 Character 值使用的扩展字形集群意味着字符串的创建和修改可能不会总是影响字符串的字符统计数。
NSString.length
对于很有历史感的 NSString 来说,就没那么复杂了,它就是字符的数组,所以它只会单纯计算字符数量,由于一个 Emoji 就是用两个字符的长度表达的,所以在这里,长度为 2 :
1
2
3
4
5
|
let s = “?”
print((s as NSString).length)
//result is 2
|
兼容性
如果不注意这个问题,就会遇到很多奇怪的小错误,比如在设定富文本颜色的时候,会导致文本末尾异常,会导致 Emoji 乱码等等。因为富文本是 NSAttributedString ,它的长度计算是基于 UTF16 字符长度的,而不是合并后的【视觉】字符长度:
1
2
3
4
5
6
7
8
9
10
|
let s = “?”
let att = NSMutableAttributedString(string: s)
att.addAttributes([.foregroundColor:NSColor.red], range: NSRange(location: 0, length: s.count))
print(att)
// result is: �{
NSColor = “sRGB IEC61966-2.1 colorspace 1 0 0 1”;
}�{
}
|
注意高亮行,这里 NSRange(location: 0, length: s.count) 使用了 String 的 count 属性,读出的长度应该是 1 ,但 NSMutableAttributedString 是以 UTF16 字符长度做计算,所以字符串长度应该是 2 ,结果导致为半个 Emoji 字符添加颜色,输出内容为乱码。
1
2
3
4
5
6
7
8
9
|
let s = “?”
let att = NSMutableAttributedString(string: s)
att.addAttributes([.foregroundColor:NSColor.red], range: NSRange(location: 0, length: (s as NSString).length))
print(att)
//result is: ?{
NSColor = “sRGB IEC61966-2.1 colorspace 1 0 0 1”;
}
|
将字符串转换为 NSString 后获取 length 则得到了正确结果。
Swift 里的 UTF16 字符串长度
那么,每次使用,都要明显地写成 (“” as NSString).length 的形式吗?虽然写个 extension 也不是不行,不过,我们其实也可以直接从 String 原生地获取这个长度:
1
2
3
4
5
|
let s = “?”
print(s.utf16.count)
//result is 2
|
讨论
String 和 NSString 有很多名称类似但功能相同的方法,但得到的结果却可能并不完全一致,要小心 Swift 对字符串的处理,这些问题往往会在一些细节的地方体现出来,比如输入法移动光标的 API,其中移动 1 长度,是 1 UTF16 字符长度,比如 Emoji,计数是 2 而不是 1,如果不注意这个细节,就会导致一些自动化功能在遇到 Emoji 表情或者某些生僻中文字符时计算出错,因为这些符号视觉上是一个字,但实际上使用了可变长的多个 UTF16 字符长度。
延伸阅读
https://stackoverflow.com/a/36268059
https://stackoverflow.com/a/29833042
https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html
https://www.cnswift.org/strings-and-characters
转载请注明:逗比根据地 » String.count vs NSString.length