如何创建雨燕范围
Swift范围比NSRange
更复杂。 如果您想尝试了解这种复杂性背后的原因,请阅读此内容。 我将仅向您展示如何创建它们以及何时使用它们。
封闭范围: a...b
即使b
是类型的最大可能值(如Int.max
),此范围运算符也会创建一个同时包含元素a
和元素b
的Swift范围。 封闭范围有两种不同类型: ClosedRange
和CountableClosedRange
。
1. ClosedRange
Swift中所有范围的元素都是可比较的(即,它们符合Comparable协议)。 这样您就可以访问集合中范围内的元素。 这是一个例子:
let myRange: ClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
但是, ClosedRange
不可计数(即,它不符合Sequence协议)。 这意味着您不能使用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
但不包括元素b
。 像上面一样,有两种不同类型的半开范围: Range
和CountableRange
。
1. Range
与ClosedRange
,您可以使用Range
访问集合的元素。 例:
let myRange: Range = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
同样,您不能在Range
进行迭代,因为它只是可比较的而不是可跨越的。
2. 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])
}
NSRange
在Swift中,您仍然可以(必须)有时仍使用NSRange
(例如,在创建属性字符串时),因此了解如何制作NSRange
很有帮助。
let myNSRange = NSRange(location: 3, length: 2)
请注意,这是位置和长度 ,而不是开始索引和结束索引。 此处的示例在含义上与Swift范围3..<5
相似。 但是,由于类型不同,因此不能互换。
带字符串的范围
...
和..<
范围运算符是创建范围的简便方法。 例如:
let myRange = 1..<3
创建相同范围的长期方法是
let myRange = CountableRange(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
您可以看到这里的索引类型是Int
。 但是,这不适用于String
,因为String是由Characters组成的,并且并非所有字符的大小都相同。 (有关更多信息,请阅读此内容。)例如😀之类的表情符号比字母“ b”占用更多空间。
NSRange问题
尝试使用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"!?
笑脸需要存储两个UTF-16代码单元,因此它给出了不包含“ d”的意外结果。
快速解决方案
因此,在Swift Strings中,您可以使用Range
,而不是Range
。 字符串索引是根据特定的字符串计算得出的,因此它知道是否存在任何表情符号或扩展的字形簇。
例
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...
和...b
和..<b
在Swift 4中,事情做了一些简化。 只要可以推断范围的起点或终点,就可以将其关闭。
整数
您可以使用单面整数范围对集合进行迭代。 以下是文档中的一些示例。
// 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...
串
这也适用于字符串范围。 如果要在一端使用str.startIndex
或str.endIndex
进行范围str.startIndex
,则可以将其关闭。 编译器将推断出它。
给定
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中工作
- 字符串子字符串在Swift中如何工作
笔记
- 您不能将使用一个字符串创建的范围用于另一个字符串。
- 如您所见,String范围在Swift中很麻烦,但确实可以更好地处理表情符号和其他Unicode标量。
进一步研究
- 字符串范围示例
- 范围运算符文档
- 字符串和字符文档