如何在Swift中创build范围?
在Objective-c中,我们使用NSRange创build范围
NSRange range; 那么如何在Swift中创build范围?
更新了Swift 3
  Swift的范围比NSRange更复杂,并且在Swift 3中并没有变得简单。如果你想了解一些复杂背后的原因,阅读这个和这个 。 我只是告诉你如何创build它们,什么时候可以使用它们。 
 封闭范围: a...b 
 此范围运算符创build一个包含元素a 和元素b的Swift范围,即使b是types的最大可能值(如Int.max )。 有两种不同types的封闭范围: 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 。 像上面一样,有两种不同types的半开范围: 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中(例如,在创build属性string时)仍然使用NSRange ,所以知道如何创build一个。 
 let myNSRange = NSRange(location: 3, length: 2) 
 请注意,这是位置和长度 ,而不是开始索引和结束索引。 这里的例子在意义上类似于Swift的范围3..<5 。 但是,由于种类不同,所以不能互换。 
范围与string
  ...和..<范围操作符是创build范围的简写方法。 例如: 
 let myRange = 1..<3 
创造相同的范围的长手的方式将是
 let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3 
 你可以看到这里的索引types是Int 。 这对String不起作用,因为string是由字符组成的,并不是所有的字符都是相同的大小。  (请阅读此处了解更多信息。)例如,像😀这样的表情符号需要比字母“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解决scheme
 正因为如此,使用Swiftstring,您可以使用Range<String.Index> ,而不是Range<Int> 。 string索引是根据一个特定的string计算的,以便知道是否有任何表情符号或扩展字形集群。 
例
 var myString = "abcde" let start = myString.index(myString.startIndex, offsetBy: 1) let end = myString.index(myString.startIndex, offsetBy: 4) let myRange = start..<end myString.substring(with: 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.substring(with: myRange2) // "😀cd" 
也可以看看:
- String.Index如何在Swift 3中工作
- String子串如何在Swift 3中工作
笔记
- 您不能使用一个string在另一个string上创build的范围。
- 正如你所看到的,String范围在Swift中是很痛苦的,但它们确实可以更好地处理表情符和其他Unicode标量。
进一步研究
- string范围示例
- 范围操作员文档
- string和字符文档
Xcode 8 beta 2•Swift 3
 let myString = "Hello World" let myRange = myString.startIndex..<myString.index(myString.startIndex, offsetBy: 5) let mySubString = myString.substring(with: myRange) // Hello 
Xcode 7•Swift 2.0
 let myString = "Hello World" let myRange = Range<String.Index>(start: myString.startIndex, end: myString.startIndex.advancedBy(5)) let mySubString = myString.substringWithRange(myRange) // Hello 
或干脆
 let myString = "Hello World" let myRange = myString.startIndex..<myString.startIndex.advancedBy(5) let mySubString = myString.substringWithRange(myRange) // Hello 
(1 .. <10)
返回…
范围= 1 .. <10
像这样使用
 var start = str.startIndex // Start at the string's start index var end = advance(str.startIndex, 5) // Take start index and advance 5 characters forward var range: Range<String.Index> = Range<String.Index>(start: start,end: end) let firstFiveDigit = str.substringWithRange(range) print(firstFiveDigit) 
输出:你好
 我感到奇怪的是,即使在Swift 4中,仍然没有简单的使用Int来表示String范围的本地方法。 唯一的String方法可以让你提供一个Int作为获取范围的一个子string的方式是prefix和suffix 。 
 在手边有一些转换实用程序是非常有用的,以便我们可以在与String交谈时像NSRange一样说话。 这是一个实用程序,它接受一个位置和长度,就像NSRange一样,并返回一个Range<String.Index> : 
 func range(_ start:Int, _ length:Int) -> Range<String.Index> { let i = self.index(start >= 0 ? self.startIndex : self.endIndex, offsetBy: start) let j = self.index(i, offsetBy: length) return i..<j } 
 例如, "hello".range(0,1)"是包含"hello"的第一个字符的Range<String.Index> 。作为奖励,我允许消极位置: "hello".range(-1,1)"是包含"hello"最后一个字符的Range<String.Index> 。 
 将Range<String.Index>转换为NSRange对于那些必须与Cocoa交谈的时刻(例如处理NSAttributedString属性范围)也很有用。  Swift 4提供了一个原生的方式来做到这一点: 
 let nsrange = NSRange(range, in:s) // where s is the string 
因此,我们可以直接从string位置和长度到NSRange编写另一个实用程序:
 extension String { func nsRange(_ start:Int, _ length:Int) -> NSRange { return NSRange(self.range(start,length), in:self) } } 
你可以这样使用
 let nsRange = NSRange(location: someInt, length: someInt) 
如在
 let myNSString = bigTOTPCode as NSString //12345678 let firstDigit = myNSString.substringWithRange(NSRange(location: 0, length: 1)) //1 let secondDigit = myNSString.substringWithRange(NSRange(location: 1, length: 1)) //2 let thirdDigit = myNSString.substringWithRange(NSRange(location: 2, length: 4)) //3456 
我创build了以下扩展名:
 extension String { func substring(from from:Int, to:Int) -> String? { if from<to && from>=0 && to<self.characters.count { let rng = self.startIndex.advancedBy(from)..<self.startIndex.advancedBy(to) return self.substringWithRange(rng) } else { return nil } } } 
使用示例:
 print("abcde".substring(from: 1, to: 10)) //nil print("abcde".substring(from: 2, to: 4)) //Optional("cd") print("abcde".substring(from: 1, to: 0)) //nil print("abcde".substring(from: 1, to: 1)) //nil print("abcde".substring(from: -1, to: 1)) //nil 
 func replace(input: String, start: Int,lenght: Int, newChar: Character) -> String { var chars = Array(input.characters) for i in start...lenght { guard i < input.characters.count else{ break } chars[i] = newChar } return String(chars) 
}