如何在 Swift 中创建范围?

Posted

技术标签:

【中文标题】如何在 Swift 中创建范围?【英文标题】:How to create range in Swift? 【发布时间】:2015-07-17 14:10:17 【问题描述】:

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

NSRange range;

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

【问题讨论】:

你需要这个范围做什么? Swift 字符串使用Range<String.Index>,但有时需要使用NSStringNSRange,所以更多的上下文会很有用。 – 但是看看***.com/questions/24092884/…。 【参考方案1】:

这样使用

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)

输出:你好

【讨论】:

我看到他们有“范围”数据类型。那么如何使用呢? @vichhai 你能详细说明你想在哪里使用吗? 像目标c:NSRange范围;【参考方案2】:

你可以这样使用

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

【讨论】:

【参考方案3】:

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

【讨论】:

【参考方案4】:

(1..

返回...

范围 = 1..

【讨论】:

这如何回答这个问题?【参考方案5】:

为 Swift 4 更新

Swift 范围比 NSRange 更复杂,而且在 Swift 3 中也没有变得更容易。如果您想尝试了解其中一些复杂性背后的原因,请阅读 this 和 this。我将向您展示如何创建它们以及何时可以使用它们。

封闭范围:a...b

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

1。 ClosedRange

Swift 中所有范围的元素都是可比较的(即它们符合 Comparable 协议)。这允许您访问集合中范围内的元素。这是一个例子:

let myRange: ClosedRange = 1...3

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

但是,ClosedRange 是不可数的(即,它不符合序列协议)。这意味着您不能使用 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..&lt;b

此范围运算符包含元素a,但不包含元素b。和上面一样,有两种不同类型的半开范围:RangeCountableRange

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(例如,在制作 attributed strings 时),所以知道如何制作是很有帮助的。

let myNSRange = NSRange(location: 3, length: 2)

注意这是位置和长度,而不是开始索引和结束索引。此处的示例与 Swift 范围 3..&lt;5 的含义相似。但是,由于类型不同,它们是不可互换的。

字符串范围

.....&lt; 范围运算符是创建范围的简写方式。例如:

let myRange = 1..<3

创建相同范围的常用方法是

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

可以看到这里的索引类型是Int。不过,这不适用于String,因为字符串是由字符组成的,而且并非所有字符的大小都相同。 (阅读this 了解更多信息。)例如,像? 这样的表情符号比字母“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&lt;String.Index&gt;,而不是 Range&lt;Int&gt;。字符串索引是根据特定字符串计算的,以便它知道是否有任何表情符号或扩展字素簇。

例子

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..&lt;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.startIndexstr.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

另见:

How does String.Index work in Swift How does String substring work in Swift

备注

您不能使用由一个字符串在不同字符串上创建的范围。 如您所见,字符串范围在 Swift 中是一个难题,但它们确实可以更好地处理表情符号和其他 Unicode 标量。

进一步研究

String Range examples Range Operator documentation Strings and Characters documentation

【讨论】:

我的评论与“NSRange 问题”小节有关。 NSString 在内部以 UTF-16 编码存储其字符。完整的 unicode 标量是 21 位的。笑脸字符 (U+1F600) 不能存储在单个 16 位代码单元中,因此它分布在 2 上。NSRange 基于 16 位代码单元进行计数。在这个例子中,3 个代码单元恰好只代表 2 个字符【参考方案6】:

我创建了以下扩展:

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

【讨论】:

【参考方案7】:
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)

【讨论】:

【参考方案8】:

令我惊讶的是,即使在 Swift 4 中,仍然没有使用 Int 表示字符串范围的简单本地方法。只有prefixsuffix 是让您提供 Int 作为按范围获取子字符串的一种方式的 String 方法。

手头有一些转换工具很有用,这样我们在与字符串对话时可以像 NSRange 一样对话。这是一个获取位置和长度的实用程序,就像 NSRange 一样,并返回 Range&lt;String.Index&gt;

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&lt;String.Index&gt;。作为奖励,我允许否定位置:"hello".range(-1,1)" 是包含"hello" 的最后一个字符的Range&lt;String.Index&gt;

Range&lt;String.Index&gt; 转换为 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)
    

【讨论】:

【参考方案9】:

如果有人想创建 NSRange 对象可以创建为:

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

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

【讨论】:

【参考方案10】:

我想这样做:

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

【讨论】:

以上是关于如何在 Swift 中创建范围?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中创建私有函数? [复制]

如何在 Swift 中创建 UIAlertView?

如何在 Swift 3 中创建调度队列

如何在 Swift 中创建一个圆形按钮? [关闭]

如何在 Swift 中创建连接超时?

如何在 Swift 中创建延迟?