Swift查找子字符串的所有匹配项

Swift查找子字符串的所有匹配项,swift,string,swift3,Swift,String,Swift3,这里有一个Swift中String类的扩展,它返回给定子字符串的第一个字母的索引 有人能帮我做一下吗?这样它就可以返回所有事件的数组,而不仅仅是第一个事件 多谢各位 extension String { func indexOf(string : String) -> Int { var index = -1 if let range = self.range(of : string) { if !range.isEmpty

这里有一个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
    }
}

例如,不是返回值
50
而是像
[50、74、91、103]
这样的函数,这里有两个函数。一个返回
[Range]
,另一个返回
[Range]
。如果你不需要前者,你可以把它保密。我将其设计为模仿
范围(of:options:range:locale:)
方法,因此它支持所有相同的功能

import Foundation

extension String {
    public 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 = 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
    }

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


    private func indexRangeToIntRange(_ range: Range<String.Index>) -> Range<Int> {
        return indexToInt(range.lowerBound) ..< indexToInt(range.upperBound)
    }

    private func indexToInt(_ index: String.Index) -> Int {
        return self.distance(from: self.startIndex, to: index)
    }
}

let s = "abc abc  abc   abc    abc"
print(s.allRanges(of: "abc") as [Range<String.Index>])
print()
print(s.allRanges(of: "abc") as [Range<Int>])
<代码>导入基础 扩展字符串{ 公共职能所有范围( 阿斯汀:弦, 选项:String.CompareOptions=[], 范围:范围?=零, 语言环境:语言环境?=nil )->[范围]{ //要在其中搜索的切片 让切片=(范围==nil)?self[…]:self[range!] var previousEnd=s.startIndex 变量范围=[范围]() 当r=slice.range时( of:aString,options:options, 范围:previousEnd..[范围]{ 返回所有范围(of:aString,options:options,range:range,locale:locale) .map(索引范围到范围) } 私有函数索引rangeToIntrange(range:range)->range{ 返回索引点(范围.下限)…<索引点(范围.上限) } private func indexpoint(u-index:String.index)->Int{ 返回self.distance(从:self.startIndex到:index) } } let s=“abc” 打印(s.allRanges(属于“abc”)为[范围]) 打印() 打印(s.allRanges(属于“abc”)为[范围])
您只需不断推进搜索范围,直到找不到子字符串的更多实例:

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]
扩展字符串{
func indicatesof(字符串:字符串)->[Int]{
var指数=[Int]()
var searchStartIndex=self.startIndex
当searchStartIndex让range=self.range(of:string,range:searchStartIndex..这可以通过递归方法完成。我使用了一个数字字符串来测试它。它返回一个可选数组
Int
,这意味着如果找不到子字符串,它将为零

extension String {
    func indexes(of string: String, offset: Int = 0) -> [Int]? {
        if let range = self.range(of : string) {
            if !range.isEmpty {
                let index = distance(from : self.startIndex, to : range.lowerBound) + offset
                var result = [index]
                let substr = self.substring(from: range.upperBound)
                if let substrIndexes = substr.indexes(of: string, offset: index + distance(from: range.lowerBound, to: range.upperBound)) {
                    result.append(contentsOf: substrIndexes)
                }
                return result
            }
        }
        return nil
    }
}

let numericString = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
numericString.indexes(of: "3456")

实际上没有一个内置函数来实现这一点,但是我们可以实现一个modified函数来获得我们想要匹配的字符串的所有索引。它也应该非常有效,因为我们不需要重复调用字符串上的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
    }
}
扩展字符串{
func indicatesof(字符串:字符串)->[Int]{
//转换为utf8字符数组使标记和比较更加容易
让search=self.utf8.map{$0}
让word=string.utf8.map{$0}
var指数=[Int]()
//m-搜索字符串中当前匹配项的开头
//i-当前字符在我们试图匹配的字符串中的位置
var m=0,i=0
而m+i
我知道我们不是在玩代码高尔夫,但对于那些对不使用
var
s或循环的函数式单行实现感兴趣的人来说,这是另一种可能的解决方案:

extension String {
    func indices(of string: String) -> [Int] {
        return indices.reduce([]) { $1.encodedOffset > ($0.last ?? -1) && self[$1...].hasPrefix(string) ? $0 + [$1.encodedOffset] : $0 }
    }
}

我已经调整了接受的答案,以便可以配置
区分大小写

extension String {
    func allIndexes(of subString: String, caseSensitive: Bool = true) -> [Int] {
        let subString = caseSensitive ? subString : subString.lowercased()
        let mainString = caseSensitive ? self : self.lowercased()
        var indices = [Int]()
        var searchStartIndex = mainString.startIndex
        while searchStartIndex < mainString.endIndex,
            let range = mainString.range(of: subString, range: searchStartIndex..<mainString.endIndex),
            !range.isEmpty
        {
            let index = distance(from: mainString.startIndex, to: range.lowerBound)
            indices.append(index)
            searchStartIndex = range.upperBound
        }

        return indices
    }
}
扩展字符串{
func-allIndexes(子字符串的类型:String,区分大小写:Bool=true)->[Int]{
let subString=区分大小写?subString:subString.lowercased()
让mainString=区分大小写?self:self.lowercased()
var指数=[Int]()
var searchStartIndex=mainString.startIndex
当searchStartIndex让range=mainString.range(of:subString,range:searchStartIndex..请检查以下答案,以便在多个位置查找多个项目

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
}

func attributedStringWithColor(_ strings: [String], color: UIColor, characterSpacing: UInt? = nil) -> NSAttributedString {
    let attributedString = NSMutableAttributedString(string: self)
    for string in strings {
        let indexes = self.indicesOf(string: string)
        for index in indexes {
            let range = NSRange(location: index, length: string.count)
            attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: range)
        }
    }
    
    guard let characterSpacing = characterSpacing else {return attributedString}
    
    attributedString.addAttribute(NSAttributedString.Key.kern, value: characterSpacing, range: NSRange(location: 0, length: attributedString.length))
    
    return attributedString
}

并获得结果

哨兵值,如
-1
在Swift中没有位置。这就是选项的作用。
而let
.brilliant.Hmm。这似乎失败了。
let keyword=“hello”let html=“My websitehellohello”let indicies=html.indiciesof(string:keyword)print(indicies)
致命错误:无法增加超过endIndex的值
更简单的示例:
let keyword=“A”let html=“A”let indicies=html。indiciesof(字符串:关键字)打印(标记)
致命错误:不能在endIndex之外递增
如果将两个相同的关键字放在一行中,它将只找到一个。但是如果将三个关键字放在一行中,它会找到两个。尽管如此,看起来要好得多。@Eugenio如果允许子字符串重叠,则每次将搜索开始索引提前一个字符:
searchStartIndex=string.index(之后:searchStartIndex)
这不需要递归。这只是一个不必要的内存命中。Oooo非常好,我感谢您自己实现了这一点。
我们不需要重复调用字符串上的range
这可能很好,很可能
range
也只进行KMP搜索。这很好!我注意到一个小缺点:如果哟
let message = "Item 1 + Item 2 + Item 3"
message.attributedStringWithColor(["Item", "+"], color: UIColor.red)