更新为 Swift 4
Swift 范围比NSRange
,并且它们在 Swift 3 中并没有变得更容易。如果你想尝试理解这种复杂性背后的原因,请阅读this https://oleb.net/blog/2016/09/swift-3-ranges/ and this https://github.com/apple/swift-evolution/blob/master/proposals/0065-collections-move-indices.md。我将向您展示如何创建它们以及何时可以使用它们。
封闭范围:a...b
This 范围运算符 https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html创建一个 Swift 范围,其中包含两个元素a
and元素b
, 即使b
是类型的最大可能值(例如Int.max
)。有两种不同类型的封闭范围:ClosedRange
and CountableClosedRange
.
1. ClosedRange
Swift 中所有范围的元素都是可比较的(即它们符合 Comparable 协议)。这允许您访问集合中范围内的元素。这是一个例子:
let myRange: ClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
然而,一个ClosedRange
不可数(即不符合Sequence协议)。这意味着您不能使用 a 迭代元素for
环形。为此,您需要CountableClosedRange
.
2. CountableClosedRange
这与上一个类似,只是现在范围也可以迭代。
let myRange: CountableClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
for index in myRange {
print(myArray[index])
}
半开范围:a..<b
该范围运算符包括元素a
but not元素b
。与上面一样,有两种不同类型的半开范围:Range
and CountableRange
.
1. Range
As with ClosedRange
,您可以使用以下方式访问集合的元素Range
。例子:
let myRange: Range = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
但同样,你不能迭代Range
因为它只能比较,不能跨越。
2. CountableRange
A CountableRange
允许迭代。
let myRange: CountableRange = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
for index in myRange {
print(myArray[index])
}
NSRange
您仍然可以(必须)使用NSRange
有时在 Swift 中(当制作属性字符串 https://stackoverflow.com/a/32269975/3681880,例如),因此了解如何制作一个是有帮助的。
let myNSRange = NSRange(location: 3, length: 2)
请注意,这是位置和length,不是开始索引和结束索引。这里的例子与 Swift range 的含义类似3..<5
。但由于类型不同,因此不能互换。
带字符串的范围
The ...
and ..<
范围运算符是创建范围的简写方式。例如:
let myRange = 1..<3
创建相同范围的长手方法是
let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
可以看到这里的索引类型是Int
。这不适合String
但是,因为字符串是由字符组成的,并且并非所有字符的大小都相同。 (读this https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html了解更多信息。)例如,像 ???? 这样的表情符号比字母“b”占用更多空间。
NSRange 的问题
尝试尝试NSRange
and an NSString
加上表情符号,你就会明白我的意思了。头痛。
let myNSRange = NSRange(location: 1, length: 3)
let myNSString: NSString = "abcde"
myNSString.substring(with: myNSRange) // "bcd"
let myNSString2: NSString = "a????cde"
myNSString2.substring(with: myNSRange) // "????c" Where is the "d"!?
笑脸需要两个 UTF-16 代码单元来存储,因此它给出了不包含“d”的意外结果。
迅捷解决方案
因此,对于 Swift Strings,您可以使用Range<String.Index>
, not Range<Int>
。字符串索引是根据特定字符串计算的,以便它知道是否存在任何表情符号或扩展字素簇。
Example
var myString = "abcde"
let start = myString.index(myString.startIndex, offsetBy: 1)
let end = myString.index(myString.startIndex, offsetBy: 4)
let myRange = start..<end
myString[myRange] // "bcd"
myString = "a????cde"
let start2 = myString.index(myString.startIndex, offsetBy: 1)
let end2 = myString.index(myString.startIndex, offsetBy: 4)
let myRange2 = start2..<end2
myString[myRange2] // "????cd"
单边范围:a...
and ...b
and ..<b
在 Swift 4 中,事情被简化了一些。只要可以推断出范围的起点或终点,您就可以将其保留。
Int
您可以使用单边整数范围来迭代集合。以下是一些来自文档 https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html.
// iterate from index 2 to the end of the array
for name in names[2...] {
print(name)
}
// iterate from the beginning of the array to index 2
for name in names[...2] {
print(name)
}
// iterate from the beginning of the array up to but not including index 2
for name in names[..<2] {
print(name)
}
// the range from negative infinity to 5. You can't iterate forward
// over this because the starting point in unknown.
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
// You can iterate over this but it will be an infinate loop
// so you have to break out at some point.
let range = 5...
String
这也适用于字符串范围。如果您正在制作一个范围str.startIndex
or str.endIndex
一方面,您可以将其保留。编译器会推断它。
Given
var str = "Hello, playground"
let index = str.index(str.startIndex, offsetBy: 5)
let myRange = ..<index // Hello
您可以使用以下命令从索引转到 str.endIndex...
var str = "Hello, playground"
let index = str.index(str.endIndex, offsetBy: -10)
let myRange = index... // playground
也可以看看:
- String.Index 在 Swift 中如何工作 https://stackoverflow.com/a/39676940/3681880
- String 子字符串在 Swift 中如何工作 https://stackoverflow.com/a/39677331/3681880
Notes
- 您不能在不同的字符串上使用通过一个字符串创建的范围。
- 正如您所看到的,字符串范围在 Swift 中是一个难题,但它们确实可以更好地处理表情符号和其他 Unicode 标量。
进一步研究
- 字符串范围示例 http://www.dotnetperls.com/substring-swift
- 范围运算符文档 https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html
- 字符串和字符文档 https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html