[英]How to create range in Swift?
在 Objective-c 中,我們使用 NSRange 創建范圍
NSRange range;
那么如何在 Swift 中創建范圍呢?
為 Swift 4 更新
Swift 范圍比NSRange
更復雜,而且它們在 Swift 3 中也沒有變得更容易。如果您想嘗試理解某些復雜性背后的原因,請閱讀this和this 。 我只會向您展示如何創建它們以及何時可以使用它們。
a...b
這個范圍運算符創建了一個包含元素a
和元素b
的 Swift 范圍,即使b
是某個類型的最大可能值(如Int.max
)。 有兩種不同類型的封閉范圍: ClosedRange
和CountableClosedRange
。
ClosedRange
Swift 中所有范圍的元素都是可比較的(即,它們符合 Comparable 協議)。 這允許您訪問集合中范圍內的元素。 下面是一個例子:
let myRange: ClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
但是, ClosedRange
是不可數的(即,它不符合 Sequence 協議)。 這意味着您不能使用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
(例如,在制作屬性字符串時),因此知道如何制作它會很有幫助。
let myNSRange = NSRange(location: 3, length: 2)
請注意,這是 location 和length ,而不是開始索引和結束索引。 此處的示例與 Swift 范圍3..<5
含義相似。 但是,由於類型不同,它們不能互換。
...
和..<
范圍運算符是創建范圍的一種簡寫方式。 例如:
let myRange = 1..<3
創建相同范圍的長期方法是
let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
可以看到這里的索引類型是Int
。 但是,這不適用於String
,因為 Strings 由 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 字符串,您使用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...
和...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
筆記
Xcode 8 測試版 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
我感到驚訝的是,即使在 Swift 4 中,仍然沒有簡單的本地方式來使用 Int 來表達 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)
}
}
像這樣使用
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)
輸出:你好
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)
}
如果有人想創建 NSRange 對象可以創建為:
let range: NSRange = NSRange.init(location: 0, length: 5)
這將創建位置為 0 和長度為 5 的范圍
你可以這樣使用
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
我創建了以下擴展:
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
我想做這個:
print("Hello"[1...3])
// out: Error
但不幸的是,我不能自己寫下標,因為討厭的下標占用了名稱空間。
但是我們可以這樣做:
print("Hello"[range: 1...3])
// out: ell
只需將其添加到您的項目中:
extension String {
subscript(range: ClosedRange<Int>) -> String {
get {
let start = String.Index(utf16Offset: range.lowerBound, in: self)
let end = String.Index(utf16Offset: range.upperBound, in: self)
return String(self[start...end])
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.