Updated for Swift 4
Swift 射程比NSRange
更复杂,在Swift 3中也没有变得更简单.如果你想try 理解这种复杂性背后的原因,请阅读this和this.我将向您展示如何创建它们以及何时可以使用它们.
a...b
这range operator创建了一个Swift范围,其中包括元素a
、and和元素b
,即使b
是类型(如Int.max
)的最大可能值.有两种不同类型的闭合范围:ClosedRange
和CountableClosedRange
.
ClosedRange
Swift中所有范围的要素都具有可比性(即,它们符合可比协议).允许您从集合访问范围内的元素.下面是一个例子:
let myRange: ClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
然而,ClosedRange
是不可计数的(即,它不符合序列协议).这意味着不能用for
循环迭代元素.为此,你需要CountableClosedRange
.
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
,但不包括元素b
.如上所述,有两种不同类型的半开放范围:Range
和CountableRange
.
Range
与ClosedRange
一样,您可以使用Range
访问集合的元素.例子:
let myRange: Range = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
不过,你不能重复Range
,因为它只是可比较的,而不是可跨越的.
CountableRange
CountableRange
允许迭代.
let myRange: CountableRange = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
for index in myRange {
print(myArray[index])
}
在Swift中,您有时仍然可以(必须)使用NSRange
(例如,在制作attributed strings时),因此了解如何制作NSRange
是很有帮助的.
let myNSRange = NSRange(location: 3, length: 2)
请注意,这是位置和length,而不是开始索引和结束索引.这里的示例与Swift 3..<5
的含义类似.然而,由于类型不同,它们不能互换.
...
和..<
范围运算符是创建范围的简写方法.例如:
let myRange = 1..<3
创建相同范围的长期方法是
let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
你可以看到这里的索引类型是Int
.不过,这对String
不起作用,因为字符串是由字符组成的,而且并非所有字符的大小都相同.(更多信息请阅读this.)表情符号?, 例如,比字母"b"占用更多空间.
Problem with NSRange
try 用表情符号NSRange
和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"!?
smiley face需要存储两个UTF-16代码单元,因此它给出了不包含"d"的意外结果.
Swift Solution
因此,对于Swift字符串,您使用Range<String.Index>
,而不是Range<Int>
.字符串索引是基于特定字符串计算的,这样它就知道是否存在任何表情符号或扩展的字形集群.
实例
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
可以使用单边整数范围来迭代集合.下面是documentation人中的一些例子.
// 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
或str.endIndex
的范围,你可以把它关掉.编译器将推断出它.
鉴于
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
Notes