How to create range in Swift?

在 Objective-c 中,我们使用 NSRange 创建范围

NSRange range;

那么如何在 Swift 中创建范围呢?

148991 次浏览

像这样使用

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)

输出: Hello

你可以这样用

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 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)

归还..。

Range = 1..<10

更新为 Swift 4

快速范围比 NSRange更复杂,而且在快速3中也没有变得更容易。如果您想尝试理解这种复杂性背后的原因,请阅读 这个这个。我将向您展示如何创建它们,以及什么时候可以使用它们。

近距离: a...b

这个 range operator创建一个 Swift 范围,其中包括元素 a 还有元素 b,即使 b是类型(如 Int.max)的最大可能值。有两种不同类型的封闭范围: ClosedRangeCountableClosedRange

1. ClosedRange

The elements of all ranges in Swift are comparable (ie, they conform to the Comparable protocol). That allows you to access the elements in the range from a collection. Here is an example:

let myRange: ClosedRange = 1...3


let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]

但是,ClosedRange是不可数的(即,它不符合序列协议)。这意味着不能使用 for循环迭代元素。为此,你需要 CountableClosedRange

2. CountableClosedRange

This is similar to the last one except now the range can also be iterated over.

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

This range operator includes element a but 没有 element b. Like above, there are two different types of half-open ranges: Range and 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)

Note that this is location and length, not start index and end index. The example here is similar in meaning to the Swift range 3..<5. However, since the types are different, they are not interchangeable.

Ranges with Strings

.....<范围运算符是创建范围的简写方法。例如:

let myRange = 1..<3

创建相同范围的长手方法是

let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3

您可以看到这里的索引类型是 Int。但是,这对 String不起作用,因为字符串是由字符组成的,并非所有字符都是相同大小的。(阅读 这个了解更多信息。)例如,一个表情符号比字母“ b”占用更多的空间。

NSRange 的问题

尝试使用 NSRangeNSString表情符号,你就会明白我的意思。头痛。

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 Solution

因此,对于 Swift String,您使用的是 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"

One-sided Ranges: a... and ...b and ..<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...

绳子

这也适用于 String 范围。如果你正在制作一个范围与 str.startIndexstr.endIndex在一端,你可以把它关闭。编译器会推断出来。

Given

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 标量。

进一步研究

I created the following extension:

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 表示字符串范围的简单本机方法。只有 prefixsuffix方法允许提供 Int 作为通过范围获取子字符串的方法。

手边有一些转换实用程序是很有用的,这样我们在对 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)
}
}

如果任何人想创建 NSRange 对象,可以创建如下:

let range: NSRange = NSRange.init(location: 0, length: 5)

这将创建位置0和长度5的范围

我想这么做:

print("Hello"[1...3])
// out: Error

But unfortunately, I can't write a subscript of my own because the loathed one takes up the name space.

然而,我们可以这样做:

print("Hello"[range: 1...3])
// out: ell

Just add this to your project:

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])
}
}
}