如何在 Swift 中创建范围?
How to create range in Swift?
在 Objective-c 中,我们使用 NSRange
创建范围
NSRange range;
那么如何在 Swift 中创建范围?
这样使用
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)
输出:你好
你可以这样使用
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
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)
returns...
Range = 1..<10
更新 Swift 4
Swift 范围比 NSRange
更复杂,并且它们在 Swift 中并没有变得更容易 3. 如果您想尝试理解其中一些背后的原因复杂性,阅读 this and this。我将只向您展示如何创建它们以及何时可以使用它们。
闭合范围:a...b
此 range operator 创建一个 Swift 范围,其中包括元素 a
和 元素 b
,即使 b
是类型的最大可能值(如 Int.max
)。有两种不同类型的闭合范围: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
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])
}
NS 范围
您仍然可以(必须)在 Swift 中有时仍然使用 NSRange
(例如,当制作 attributed strings 时),因此了解如何制作一个很有帮助。
let myNSRange = NSRange(location: 3, length: 2)
注意这是位置和长度,不是开始索引和结束索引。此处的示例在含义上类似于 Swift 范围 3..<5
。但由于类型不同,不能互换。
带字符串的范围
...
和 ..<
范围运算符是一种 shorthand 创建范围的方法。例如:
let myRange = 1..<3
创建相同范围的长期方法是
let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
可以看到这里的索引类型是Int
。不过,这对 String
不起作用,因为字符串是由字符组成的,并非所有字符的大小都相同。 (阅读 this 了解更多信息。)例如,像 space 这样的表情符号比字母 "b".
占用更多 space
NSRange 问题
尝试使用 NSRange
和带有表情符号的 NSString
,您就会明白我的意思。头痛。
let myNSRange = NSRange(location: 1, length: 3)
let myNSString: NSString = "abcde"
myNSString.substring(with: myNSRange) // "bcd"
let myNSString2: NSString = "acde"
myNSString2.substring(with: myNSRange) // "c" Where is the "d"!?
笑脸需要两个 UTF-16 代码单元来存储,因此它给出了不包括 "d".
的意外结果
Swift解法
因此,对于 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 = "acde"
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 件事被简化了一点。只要可以推断范围的起点或终点,就可以将其关闭。
整数
您可以使用单边整数范围来迭代集合。以下是 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...
字符串
这也适用于字符串范围。如果您要在一端设置 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
另请参阅:
注释
- 您不能将用一个字符串创建的范围用于另一个字符串。
- 如您所见,字符串范围在 Swift 中是一个难题,但它们确实可以更好地处理表情符号和其他 Unicode 标量。
进一步研究
我创建了以下扩展:
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)
}
令我惊讶的是,即使在 Swift 4 中,仍然没有使用 Int 表达字符串范围的简单本地方法。唯一允许您提供 Int 作为按范围获取子字符串的方法的 String 方法是 prefix
和 suffix
.
手头有一些转换实用程序很有用,这样我们就可以像 NSRange 一样与 String 对话。这是一个使用位置和长度的实用程序,就像 NSRange 和 returns a 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)"
是 Range<String.Index>
包含 "hello"
的第一个字符。作为奖励,我允许使用否定位置:"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
因此我们可以编写另一个实用程序,我们直接从字符串位置和长度转到 NSRange:
extension String {
func nsRange(_ start:Int, _ length:Int) -> NSRange {
return NSRange(self.range(start,length), in:self)
}
}
如果有人想创建 NSRange 对象可以创建为:
let range: NSRange = NSRange.init(location: 0, length: 5)
这将创建位置为 0、长度为 5 的范围
我想这样做:
print("Hello"[1...3])
// out: Error
但很遗憾,我不能自己写一个下标,因为讨厌的人占用了名字space。
但是我们可以这样做:
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])
}
}
}
在 Objective-c 中,我们使用 NSRange
创建范围NSRange range;
那么如何在 Swift 中创建范围?
这样使用
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)
输出:你好
你可以这样使用
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
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)
returns...
Range = 1..<10
更新 Swift 4
Swift 范围比 NSRange
更复杂,并且它们在 Swift 中并没有变得更容易 3. 如果您想尝试理解其中一些背后的原因复杂性,阅读 this and this。我将只向您展示如何创建它们以及何时可以使用它们。
闭合范围:a...b
此 range operator 创建一个 Swift 范围,其中包括元素 a
和 元素 b
,即使 b
是类型的最大可能值(如 Int.max
)。有两种不同类型的闭合范围: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
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])
}
NS 范围
您仍然可以(必须)在 Swift 中有时仍然使用 NSRange
(例如,当制作 attributed strings 时),因此了解如何制作一个很有帮助。
let myNSRange = NSRange(location: 3, length: 2)
注意这是位置和长度,不是开始索引和结束索引。此处的示例在含义上类似于 Swift 范围 3..<5
。但由于类型不同,不能互换。
带字符串的范围
...
和 ..<
范围运算符是一种 shorthand 创建范围的方法。例如:
let myRange = 1..<3
创建相同范围的长期方法是
let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
可以看到这里的索引类型是Int
。不过,这对 String
不起作用,因为字符串是由字符组成的,并非所有字符的大小都相同。 (阅读 this 了解更多信息。)例如,像 space 这样的表情符号比字母 "b".
NSRange 问题
尝试使用 NSRange
和带有表情符号的 NSString
,您就会明白我的意思。头痛。
let myNSRange = NSRange(location: 1, length: 3)
let myNSString: NSString = "abcde"
myNSString.substring(with: myNSRange) // "bcd"
let myNSString2: NSString = "acde"
myNSString2.substring(with: myNSRange) // "c" Where is the "d"!?
笑脸需要两个 UTF-16 代码单元来存储,因此它给出了不包括 "d".
的意外结果Swift解法
因此,对于 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 = "acde"
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 件事被简化了一点。只要可以推断范围的起点或终点,就可以将其关闭。
整数
您可以使用单边整数范围来迭代集合。以下是 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...
字符串
这也适用于字符串范围。如果您要在一端设置 str.startIndex
或 str.endIndex
的范围,则可以将其关闭。编译器会推断出来。
给出
var str = "Hello, playground"
let index = str.index(str.startIndex, offsetBy: 5)
let myRange = ..<index // Hello
您可以使用 ...
var str = "Hello, playground"
let index = str.index(str.endIndex, offsetBy: -10)
let myRange = index... // playground
另请参阅:
注释
- 您不能将用一个字符串创建的范围用于另一个字符串。
- 如您所见,字符串范围在 Swift 中是一个难题,但它们确实可以更好地处理表情符号和其他 Unicode 标量。
进一步研究
我创建了以下扩展:
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)
}
令我惊讶的是,即使在 Swift 4 中,仍然没有使用 Int 表达字符串范围的简单本地方法。唯一允许您提供 Int 作为按范围获取子字符串的方法的 String 方法是 prefix
和 suffix
.
手头有一些转换实用程序很有用,这样我们就可以像 NSRange 一样与 String 对话。这是一个使用位置和长度的实用程序,就像 NSRange 和 returns a 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)"
是 Range<String.Index>
包含 "hello"
的第一个字符。作为奖励,我允许使用否定位置:"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
因此我们可以编写另一个实用程序,我们直接从字符串位置和长度转到 NSRange:
extension String {
func nsRange(_ start:Int, _ length:Int) -> NSRange {
return NSRange(self.range(start,length), in:self)
}
}
如果有人想创建 NSRange 对象可以创建为:
let range: NSRange = NSRange.init(location: 0, length: 5)
这将创建位置为 0、长度为 5 的范围
我想这样做:
print("Hello"[1...3])
// out: Error
但很遗憾,我不能自己写一个下标,因为讨厌的人占用了名字space。
但是我们可以这样做:
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])
}
}
}