查找并返回Swift中给定字符串的所有出现范围

在谈论字符串模式匹配识别问题时,您想到的第一个想法可能是Knuth–Morris–Pratt算法。 但是,在本文中,我想介绍一种基于range(of:options:range:locale:)的更直观的方法,该方法是Swift标准库的一部分。

多次返回第一次出现以收集所有它们

如果您查找Apple Developer Documentation,则会发现对函数range(of:options:range:locale:)的描述,该描述与下面的引用相同。

使用给定的语言环境(如果有),在给定的选项的约束下,找到并返回给定字符串在给定字符串范围内第一次出现的范围。

是的,它只返回第一次出现 。 因此,为了实现我们的目的,我们需要构造一个while循环以迭代接收器中给定字符串的所有出现。 参考代码段将类似于以下代码块。 并请注意,还添加了偏移量以减少每次迭代中的范围长度。 因此,实际上,每个循环的位置基本上都是基于上一次迭代的上限。

 extension String { 
var indices = [Int]()
while let range = range(of: occurrence, range: position..<endIndex) {
to: range.lowerBound)
let offset = occurrence.distance(from: occurrence.startIndex,
guard let after = index(range.lowerBound,
limitedBy: endIndex) else {
}
}
}

将索引转换为范围

在将上述代码应用于实际情况时,有时返回所有索引不足以进行文本编辑中的查找和替换所有操作。 因此,转换助手可以帮助生成范围数组,该数组更易于处理子字符串。

 extension String { 
let _indices = indices(of: searchString)
return _indices.map({ index(startIndex, offsetBy: $0)..<index(startIndex, offsetBy: $0+count) })
}

在Swift 4之前从Range创建NSRange

如果您已将Xcode转换为版本9,则Swift 4Swift标准库提供了一种将Range直接转换为NSRange 。 这绝对是许多开发人员都期待的功能,因为有时在某些函数调用尚未完全迁移到本机Swift时 ,仍不可避免地要处理NSRange ,例如下面的NSAttributedString示例。

 let str = "Hello, playground! Hello, world! Hello, earth!" 
attributedString.enumerateAttributes(in: NSRange(location: 0,
options: NSAttributedString.EnumerationOptions.longestEffectiveRangeNotRequired) { (attributes, range, stop) in
}

为了使从当前版本的SwiftSwift 4的迁移更加顺畅。 在Xcode 9正式发布之前,我们可以实现语法兼容功能以从Range创建NSRange

 extension NSRange { 
self.init()
let endIndex = range.upperBound.samePosition(in: string.utf16)
to: range.lowerBound)
}

在操场上测试以上功能

基本上,以上所有代码都是我们需要查找并返回给定字符串所有出现范围的必要元素。 最后,让我们测试下面的代码片段并查看结果。

 let str = "Hello, playground, Hello, playground, Hello, playground, Hello, playground, Hello, playground" 
print("[Int] : \(indices)")
let ranges = str.ranges(of: "playground")
print("`Range` : " + str.substring(with: range))

print("`NSRange` : " + (str as NSString).substring(with: range))

日志控制台将在操场上像这样显示。

 [Int] : [12, 31, 50, 69, 88] 
`Range` : playground
`Range` : playground
`NSRange` : playground
`NSRange` : playground
`NSRange` : playground