快速获取字符串中子字符串的所有范围

Posted

技术标签:

【中文标题】快速获取字符串中子字符串的所有范围【英文标题】:get all ranges of a substring in a string in swift 【发布时间】:2016-08-20 07:29:27 【问题描述】:

我有一个字符串,例如“ab ad adk fda kla kad ab ab kd”。我想得到 ab 的所有范围。(这里 ab 出现在 3 个位置,所以我应该得到 3 个范围)。在正常情况下,我的代码工作正常,但如果搜索文本是“。”,那么我得到错误的结果

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 
    ...

【问题讨论】:

你需要展示你所尝试的并解释它做错了什么 请详细说明你想要什么结果? A . 是一个特殊字符,意思是“任何字符” 你知道什么是正则表达式吗?这不是普通的逐个字符的字符串搜索... 【参考方案1】:

以下方案使用原生 Swift 4 函数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 然后提供原生 API 来将 Range&lt;Index&gt; 转换为 NSRange

【讨论】:

在使用正则表达式的某些情况下,这可能会导致无限循环。检查此答案以了解如何避免它***.com/a/32306142/2303865 @LeoDabus 我已经确定了一种可能发生这种情况的情况,因为确实某些正则表达式返回一个有效范围,即使要搜索的范围是空的。我已经更新了代码以反映这一点。如果这不是您最初的问题,您介意分享导致该问题的正则表达式吗? 如果我没记错的话,如果字符串末尾有一个空范围,就会发生这种情况。抱歉,我找不到问题/正则表达式。 @LeoDabus 我明白了,不用担心,我所做的编辑应该可以很好地解决问题;)【参考方案2】:

斯威夫特 5:

最受欢迎答案的改进版:

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
    

【讨论】:

【参考方案3】:

我会建议这样的解决方案:

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

除了主要问题,这段代码还展示了将NSRange 转换为Range&lt;String.Index&gt; 和从Range&lt;String.Index&gt; 转换的方法(基于this post)。

【讨论】:

它会处理所有其他特殊字符吗? 我使用NSRegularExpression.escapedPatternForString() 来转义任何可能的模式元字符。这一定是一个可靠的解决方案。 那些范围 NSRange 转换看起来很熟悉 ***.com/a/30404532/1187415 :) 这可能是正确的起源。我已经添加了对帖子的引用。【参考方案4】:

您正在使用正则表达式,因此您需要注意具有特殊含义的字符 - . 只是其中之一。

如果您正在搜索子字符串,我建议改用旧的 rangeOf... 方法:

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

只需继续在您的字符串上调用该方法(并调整searchRange),直到找不到更多匹配项。

【讨论】:

【参考方案5】:

您可以通过以下代码获取特定字符串的出现次数:

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

【讨论】:

不,我想获取范围,以便可以在该范围上应用属性。

以上是关于快速获取字符串中子字符串的所有范围的主要内容,如果未能解决你的问题,请参考以下文章

如何在字符串中查找子字符串,其中子字符串位于 Access Query 的单独表中?

C语言 计算字符串中子串出现的次数 求更改

如何在swift3中获取子字符串的范围? [复制]

Acwing周赛分享

如何获取 hive 中子字符串的计数

Java中字符串中子串的查找共有四种方法(indexof())