Swift中,字符串是不能使用整数索引的,比如下面的代码会报错:
let name = "Neil"
name[2]
// 'subscript(_:)' is unavailable: cannot subscript String with an Int, use a String.Index instead.
这是为什么呢? 简单的回答是:Swift认为字符串是由一个个字形群集 (grapheme clusters)组成的,字形群集的大小不固定所以不能用整数去索引 (字形群集其实就是Swift中的Character(字符)类).
我们先看看苹果是怎么定义字形群集的:
An extended grapheme cluster is a sequence of one or more Unicode scalars that (when combined) produce a single human-readable character.*
意思就是Swift中一个字符 (一个字形群集) 可以由一个或者多个unicode scalar组成. 比如:
let letterNorml = "é"
let letterCombining = "e\u{0301}"
for code in letterNorml.unicodeScalars {
print(code.value)
}
// 233
for code in letterCombining.unicodeScalars {
print(code.value)
}
// 101
// 769
这里了 letterNorml
字符串只有一个unicode scalar (233) 而 letterConbining
字符串有两个unicode scalar (101 和 769).
但他们是相等的:
print(letterNorml == letterCombining) // true
他们的字符(字形群集)数量相等,都只有一个:
print(letterNorml.count) // 1
print(letterCombining.count) // 1
但unicode scalars的数量不同:
print(letterNorml.unicodeScalars.count) // 1
print(letterCombining.unicodeScalars.count) // 2
因为这样,不同的字符可能需要不同数量的unicode. 比如上面的例子, 有的字母需要两个unicodes. 有的表情甚至需要4个unicodes.
这样导致一个字符需要变长度的内存空间存储,让索引变得困难。下面解释为什么:
如果一个字符是固定长度的,比如4个字节,想要找到一个字符串中位置i的字符,只需要把字符串的起始位置加上4*i就行,时间复杂度是O(1)。
然而,如果一个字符是变长度的,那我们必须从头遍历字符串中所有的unicode scalar来确定某个字符的位置,最坏情况的时间复杂度是O(n)
由于这个原因,Swift字符串不能有整数去索引
Swift字符串的索引方法
我们需要用String.Index类型来索引字符串:
let name = "Neil"
let index = name.index(name.startIndex, offsetBy: 2)
let char = name[index] // i
用Range类型来获得子字符串:
let name = "Neil"
let index1 = name.index(name.startIndex, offsetBy: 1)
let index2 = name.index(name.startIndex, offsetBy: 3)
let indexRange = index1...index2
let subString = name[indexRange] // eil