简体   繁体   中英

get all ranges of a substring in a string in swift

I have a string for example "ab ad adk fda kla kad ab ab kd". I want to get all range of ab.(Here ab is present at 3 position so I should get 3 range).In normal scenarion my code is working fine, but if search text is ".",then I am getting wrong result

do {
    let regEx = try NSRegularExpression(pattern: searchText, options: NSRegularExpressionOptions.CaseInsensitive)

    let matchesRanges = regEx.matchesInString(attributedText.string, options:[], range: NSMakeRange(0, attributedText.string.length))

    for rng in matchesRanges {
        let wordRange = rng.rangeAtIndex(0)
    }
} catch {
    ...
}

The following solution uses the native Swift 4 function range(of:, options:, range:, locale:) ):

extension String {
    func ranges(of substring: String, options: CompareOptions = [], locale: Locale? = nil) -> [Range<Index>] {
        var ranges: [Range<Index>] = []
        while ranges.last.map({ $0.upperBound < self.endIndex }) ?? true,
            let range = self.range(of: substring, options: options, range: (ranges.last?.upperBound ?? self.startIndex)..<self.endIndex, locale: locale)
        {
            ranges.append(range)
        }
        return ranges
    }
}

(Swift 4 then provides native API to convert from Range<Index> to NSRange )

Swift 5:

The improved version of the most popular answer:

extension String {    
    func ranges(of substring: String, options: CompareOptions = [], locale: Locale? = nil) -> [Range<Index>] {
        var ranges: [Range<Index>] = []
        while let range = range(of: substring, options: options, range: (ranges.last?.upperBound ?? self.startIndex)..<self.endIndex, locale: locale) {
            ranges.append(range)
        }
        return ranges
    }
}

I would suggest such a solution:

import Foundation

extension String {

    func rangesOfString(s: String) -> [Range<Index>] {
        let re = try! NSRegularExpression(pattern: NSRegularExpression.escapedPatternForString(s), options: [])
        return re.matchesInString(self, options: [], range: nsRange(startIndex ..< endIndex)).flatMap { range($0.range) }
    }

    func range(nsRange : NSRange) -> Range<Index>? {
        let utf16from = utf16.startIndex.advancedBy(nsRange.location, limit: utf16.endIndex)
        let utf16to   = utf16from.advancedBy(nsRange.length, limit: utf16.endIndex)

        if let from = String.Index(utf16from, within: self),
           let to   = String.Index(utf16to,   within: self)
        {
            return from ..< to
        } else {
            return nil
        }
    }

    func nsRange(range : Range<Index>) -> NSRange {
        let utf16from = String.UTF16View.Index(range.startIndex, within: utf16)
        let utf16to   = String.UTF16View.Index(range.endIndex,   within: utf16)
        return NSRange(location: utf16.startIndex.distanceTo(utf16from), length: utf16from.distanceTo(utf16to))
    }

}

print("[^x]? [^x]? [^x]?".rangesOfString("[^x]?")) // [Range(0..<5), Range(6..<11), Range(12..<17)]

Aside the main question, this code also shows the way to convert NSRange to and from Range<String.Index> (based on this post ).

You are using regular expressions, so you need to take care about characters that have special meaning - . is only one of them.

If you're doing a search for substrings, I suggest to use the good old rangeOf... methods instead:

func rangeOfString(_ searchString: String,
           options mask: NSStringCompareOptions,
             range searchRange: NSRange) -> NSRange

Just keep calling that method on your string (and adjust the searchRange ), until no further matches are found.

You can get occurance count for particular string by following code:

let str: NSMutableString = "ab ad adk fda kla kad ab ab kd"
let count = str.replaceOccurrencesOfString("ab", withString: "ab", options: NSStringCompareOptions.LiteralSearch, range: NSMakeRange(0, str.length))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM