我在Swift中有一个String类的扩展,它返回给定子字符串的第一个字母的索引.
任何人都可以帮助我做到这一点,它将返回所有出现的数组,而不仅仅是第一个?
谢谢.
extension String {
func indexOf(string : String) -> Int {
var index = -1
if let range = self.range(of : string) {
if !range.isEmpty {
index = distance(from : self.startIndex, to : range.lowerBound)
}
}
return index
}
}
Run Code Online (Sandbox Code Playgroud)
例如,而不是50我想要的返回值[50, 74, 91, 103]
Cod*_*ent 23
您只是继续推进搜索范围,直到找不到子字符串的更多实例:
extension String {
func indicesOf(string: String) -> [Int] {
var indices = [Int]()
var searchStartIndex = self.startIndex
while searchStartIndex < self.endIndex,
let range = self.range(of: string, range: searchStartIndex..<self.endIndex),
!range.isEmpty
{
let index = distance(from: self.startIndex, to: range.lowerBound)
indices.append(index)
searchStartIndex = range.upperBound
}
return indices
}
}
let keyword = "a"
let html = "aaaa"
let indicies = html.indicesOf(string: keyword)
print(indicies) // [0, 1, 2, 3]
Run Code Online (Sandbox Code Playgroud)
我知道我们不是在这里玩代码高尔夫,但是对于那些对不使用vars 或循环的函数式单行实现感兴趣的人,这是另一种可能的解决方案:
extension String {
func indices(of string: String) -> [Int] {
return indices.reduce([]) { $1.encodedOffset > ($0.last ?? -1) && self[$1...].hasPrefix(string) ? $0 + [$1.encodedOffset] : $0 }
}
}
Run Code Online (Sandbox Code Playgroud)
没有真正的内置函数可以做到这一点,但我们可以实现修改后的Knuth-Morris-Pratt 算法来获取我们想要匹配的字符串的所有索引。它也应该非常高效,因为我们不需要在字符串上重复调用range。
extension String {
func indicesOf(string: String) -> [Int] {
// Converting to an array of utf8 characters makes indicing and comparing a lot easier
let search = self.utf8.map { $0 }
let word = string.utf8.map { $0 }
var indices = [Int]()
// m - the beginning of the current match in the search string
// i - the position of the current character in the string we're trying to match
var m = 0, i = 0
while m + i < search.count {
if word[i] == search[m+i] {
if i == word.count - 1 {
indices.append(m)
m += i + 1
i = 0
} else {
i += 1
}
} else {
m += 1
i = 0
}
}
return indices
}
}
Run Code Online (Sandbox Code Playgroud)
这是两个功能。一个返回[Range<String.Index>],另一个返回[Range<Int>]。如果您不需要前者,可以将其设为私有。我已将其设计为模仿该range(of:options:range:locale:)方法,因此它支持所有相同的功能。
import Foundation
let s = "abc abc abc abc abc"
extension String {
func allRanges(of aString: String,
options: String.CompareOptions = [],
range: Range<String.Index>? = nil,
locale: Locale? = nil) -> [Range<String.Index>] {
//the slice within which to search
let slice = (range == nil) ? self : self[range!]
var previousEnd: String.Index? = s.startIndex
var ranges = [Range<String.Index>]()
while let r = slice.range(of: aString, options: options,
range: previousEnd! ..< s.endIndex,
locale: locale) {
if previousEnd != self.endIndex { //don't increment past the end
previousEnd = self.index(after: r.lowerBound)
}
ranges.append(r)
}
return ranges
}
func allRanges(of aString: String,
options: String.CompareOptions = [],
range: Range<String.Index>? = nil,
locale: Locale? = nil) -> [Range<Int>] {
return allRanges(of: aString, options: options, range: range, locale: locale)
.map(indexRangeToIntRange)
}
func indexToInt(_ index: String.Index) -> Int {
return self.distance(from: self.startIndex, to: index)
}
func indexRangeToIntRange(_ range: Range<String.Index>) -> Range<Int> {
return indexToInt(range.lowerBound) ..< indexToInt(range.upperBound)
}
}
print(s.allRanges(of: "abc") as [Range<String.Index>])
print(s.allRanges(of: "abc") as [Range<Int>])
Run Code Online (Sandbox Code Playgroud)
如果您只想要每个的起始索引,请继续 .map{ $0.lowerBound }
| 归档时间: |
|
| 查看次数: |
8751 次 |
| 最近记录: |