ios Swift 提取正则表达式匹配

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27880650/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 04:18:18  来源:igfitidea点击:

Swift extract regex matches

iosregexstringswift

提问by mitchkman

I want to extract substrings from a string that match a regex pattern.

我想从匹配正则表达式模式的字符串中提取子字符串。

So I'm looking for something like this:

所以我正在寻找这样的东西:

func matchesForRegexInText(regex: String!, text: String!) -> [String] {
   ???
}

So this is what I have:

所以这就是我所拥有的:

func matchesForRegexInText(regex: String!, text: String!) -> [String] {

    var regex = NSRegularExpression(pattern: regex, 
        options: nil, error: nil)

    var results = regex.matchesInString(text, 
        options: nil, range: NSMakeRange(0, countElements(text))) 
            as Array<NSTextCheckingResult>

    /// ???

    return ...
}

The problem is, that matchesInStringdelivers me an array of NSTextCheckingResult, where NSTextCheckingResult.rangeis of type NSRange.

问题是,它matchesInString为我提供了一个NSTextCheckingResult,其中NSTextCheckingResult.range类型为 的数组NSRange

NSRangeis incompatible with Range<String.Index>, so it prevents me of using text.substringWithRange(...)

NSRange与 不兼容Range<String.Index>,因此它阻止我使用text.substringWithRange(...)

Any idea how to achieve this simple thing in swift without too many lines of code?

知道如何在没有太多代码行的情况下快速实现这个简单的事情吗?

回答by Martin R

Even if the matchesInString()method takes a Stringas the first argument, it works internally with NSString, and the range parameter must be given using the NSStringlength and not as the Swift string length. Otherwise it will fail for "extended grapheme clusters" such as "flags".

即使该matchesInString()方法将 aString作为第一个参数,它也可以在内部使用NSString,并且必须使用NSString长度而不是 Swift 字符串长度来指定范围参数。否则对于“扩展的字素簇”,例如“标志”,它将失败。

As of Swift 4(Xcode 9), the Swift standard library provides functions to convert between Range<String.Index>and NSRange.

Swift 4(Xcode 9) 开始,Swift 标准库提供了在Range<String.Index>NSRange.

func matches(for regex: String, in text: String) -> [String] {

    do {
        let regex = try NSRegularExpression(pattern: regex)
        let results = regex.matches(in: text,
                                    range: NSRange(text.startIndex..., in: text))
        return results.map {
            String(text[Range(
let string = "49"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
.range, in: text)!]) } } catch let error { print("invalid regex: \(error.localizedDescription)") return [] } }

Example:

例子:

        return results.flatMap {
            Range(
func matchesForRegexInText(regex: String, text: String) -> [String] {

    do {
        let regex = try NSRegularExpression(pattern: regex, options: [])
        let nsString = text as NSString
        let results = regex.matchesInString(text,
                                            options: [], range: NSMakeRange(0, nsString.length))
        return results.map { nsString.substringWithRange(
let string = "49"
let matches = matchesForRegexInText("[0-9]", text: string)
print(matches)
// ["4", "9"]
.range)} } catch let error as NSError { print("invalid regex: \(error.localizedDescription)") return [] } }
.range, in: text).map { String(text[
func matches(for regex: String, in text: String) -> [String] {

    do {
        let regex = try NSRegularExpression(pattern: regex)
        let nsString = text as NSString
        let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
        return results.map { nsString.substring(with: 
let string = "49"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
.range)} } catch let error { print("invalid regex: \(error.localizedDescription)") return [] } }
]) } }

Note:The forced unwrap Range($0.range, in: text)!is safe because the NSRangerefers to a substring of the given string text. However, if you want to avoid it then use

注意:强制解包Range($0.range, in: text)!是安全的,因为NSRange指的是给定 string 的子字符串text。但是,如果您想避免它,请使用

//: Playground - noun: a place where people can play

import Foundation

extension String {
    func matchingStrings(regex: String) -> [[String]] {
        guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
        let nsString = self as NSString
        let results  = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
        return results.map { result in
            (0..<result.numberOfRanges).map {
                result.range(at: 
//: Playground - noun: a place where people can play

import Foundation

extension String {
    func matchingStrings(regex: String) -> [[String]] {
        guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
        let nsString = self as NSString
        let results  = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
        return results.map { result in
            (0..<result.numberOfRanges).map {
                result.rangeAt(
extension String {
    func matchingStrings(regex: String) -> [[String]] {
        guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
        let nsString = self as NSString
        let results  = regex.matchesInString(self, options: [], range: NSMakeRange(0, nsString.length))
        return results.map { result in
            (0..<result.numberOfRanges).map {
                result.rangeAtIndex(
extension String {
  func regex (pattern: String) -> [String] {
    do {
      let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions(rawValue: 0))
      let nsstr = self as NSString
      let all = NSRange(location: 0, length: nsstr.length)
      var matches : [String] = [String]()
      regex.enumerateMatchesInString(self, options: NSMatchingOptions(rawValue: 0), range: all) {
        (result : NSTextCheckingResult?, _, _) in
        if let r = result {
          let result = nsstr.substringWithRange(r.range) as String
          matches.append(result)
        }
      }
      return matches
    } catch {
      return [String]()
    }
  }
} 
).location != NSNotFound ? nsString.substringWithRange(result.rangeAtIndex(
"someText ?? pig".regex("??")
)) : "" } } } }
).location != NSNotFound ? nsString.substring(with: result.rangeAt(
["??"]
)) : "" } } } } "prefix12 aaa3 prefix45".matchingStrings(regex: "fix([0-9])([0-9])") // Prints: [["fix12", "1", "2"], ["fix45", "4", "5"]] "prefix12".matchingStrings(regex: "(?:prefix)?([0-9]+)") // Prints: [["prefix12", "12"]] "12".matchingStrings(regex: "(?:prefix)?([0-9]+)") // Prints: [["12", "12"]], other answers return an empty array here // Safely accessing the capture of the first match (if any): let number = "prefix12suffix".matchingStrings(regex: "fix([0-9]+)su").first?[1] // Prints: Optional("12")
).location != NSNotFound ? nsString.substring(with: result.range(at:
"someText ?? pig".regex("\w+")
)) : "" } } } } "prefix12 aaa3 prefix45".matchingStrings(regex: "fix([0-9])([0-9])") // Prints: [["fix12", "1", "2"], ["fix45", "4", "5"]] "prefix12".matchingStrings(regex: "(?:prefix)?([0-9]+)") // Prints: [["prefix12", "12"]] "12".matchingStrings(regex: "(?:prefix)?([0-9]+)") // Prints: [["12", "12"]], other answers return an empty array here // Safely accessing the capture of the first match (if any): let number = "prefix12suffix".matchingStrings(regex: "fix([0-9]+)su").first?[1] // Prints: Optional("12")

instead.

反而。



(Older answer for Swift 3 and earlier:)

(Swift 3 及更早版本的旧答案:)

So you should convert the given Swift string to an NSStringand then extract the ranges. The result will be converted to a Swift string array automatically.

因此,您应该将给定的 Swift 字符串转换为 an NSString,然后提取范围。结果将自动转换为 Swift 字符串数组。

(The code for Swift 1.2 can be found in the edit history.)

(Swift 1.2 的代码可以在编辑历史中找到。)

Swift 2 (Xcode 7.3.1) :

斯威夫特 2(Xcode 7.3.1):

["someText", "?", "pig"]

Example:

例子:

import Foundation

func matches(for regex: String, in text: String) -> [String] {
    do {
        let regex = try RegularExpression(pattern: regex, options: [])
        let nsString = NSString(string: text)
        let results = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length))
        return results.map { nsString.substring(with: 
func matches(for regex: String!, in text: String!) -> [String] {

    do {
        let regex = try NSRegularExpression(pattern: regex, options: [])
        let nsString = text as NSString
        let results = regex.matchesInString(text, options: [], range: NSMakeRange(0, nsString.length))
        var match = [String]()
        for result in results {
            for i in 0..<result.numberOfRanges {
                match.append(nsString.substringWithRange( result.rangeAtIndex(i) ))
            }
        }
        return match
        //return results.map { nsString.substringWithRange( 
print ( matches(for: "^(.+)\s(\d{4})" , in: "Finding Dory 2016"))
// ["Finding Dory 2016", "Finding Dory", "2016"]
.range )} //rangeAtIndex(0) } catch let error as NSError { print("invalid regex: \(error.localizedDescription)") return [] } }
.range) } } catch let error { print("invalid regex: \(error.localizedDescription)") return [] } }


Swift 3 (Xcode 8)

斯威夫特 3 (Xcode 8)

public extension String {
    public func capturedGroups(withRegex pattern: String) -> [String] {
        var results = [String]()

        var regex: NSRegularExpression
        do {
            regex = try NSRegularExpression(pattern: pattern, options: [])
        } catch {
            return results
        }
        let matches = regex.matches(in: self, options: [], range: NSRange(location:0, length: self.count))

        guard let match = matches.first else { return results }

        let lastRangeIndex = match.numberOfRanges - 1
        guard lastRangeIndex >= 1 else { return results }

        for i in 1...lastRangeIndex {
            let capturedGroupIndex = match.range(at: i)
            let matchedString = (self as NSString).substring(with: capturedGroupIndex)
            results.append(matchedString)
        }

        return results
    }
}

Example:

例子:

internal func stringsMatching(regularExpressionPattern: String, options: NSRegularExpression.Options = []) -> [String] {
        guard let regex = try? NSRegularExpression(pattern: regularExpressionPattern, options: options) else {
            return []
        }

        let nsString = self as NSString
        let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))

        return results.map {
            nsString.substring(with: 
var sample = "this is an [hello] amazing [world]"

var regex = NSRegularExpression(pattern: "\[.+?\]"
, options: NSRegularExpressionOptions.CaseInsensitive 
, error: nil)

var matches = regex?.matchesInString(sample, options: nil
, range: NSMakeRange(0, countElements(sample))) as Array<NSTextCheckingResult>

for match in matches {
   let r = (sample as NSString).substringWithRange(match.range)//cast to NSString is required to match range format.
    println("found= \(r)")
}
.range) } }

回答by Lars Blumberg

My answer builds on top of given answers but makes regex matching more robust by adding additional support:

我的答案建立在给定的答案之上,但通过添加额外的支持使正则表达式匹配更加强大:

  • Returns not only matches but returns also all capturing groupsfor each match (see examples below)
  • Instead of returning an empty array, this solution supports optional matches
  • Avoids do/catchby not printing to the console and makes use of the guardconstruct
  • Adds matchingStringsas an extension to String
  • 不仅返回匹配项,还返回每个匹配项的所有捕获组(参见下面的示例)
  • 此解决方案不返回空数组,而是支持可选匹配
  • do/catch通过不打印到控制台来避免并使用guard构造
  • matchingStrings作为扩展添加String

Swift 4.2

斯威夫特 4.2

extension String {
    func matches(regex: String) -> [String] {
        guard let regex = try? NSRegularExpression(pattern: regex, options: [.caseInsensitive]) else { return [] }
        let matches  = regex.matches(in: self, options: [], range: NSMakeRange(0, self.count))
        return matches.map { match in
            return String(self[Range(match.range, in: self)!])
        }
    }
}

Swift 3

斯威夫特 3

extension String {
    func match(_ regex: String) -> [[String]] {
        let nsString = self as NSString
        return (try? NSRegularExpression(pattern: regex, options: []))?.matches(in: self, options: [], range: NSMakeRange(0, count)).map { match in
            (0..<match.numberOfRanges).map { match.range(at: 
"prefix12suffix fix1su".match("fix([0-9]+)su")
).location == NSNotFound ? "" : nsString.substring(with: match.range(at:
[["fix12su", "12"], ["fix1su", "1"]]

// First element of sub-array is the match
// All subsequent elements are the capture groups
)) } } ?? [] } }

Swift 2

斯威夫特 2

##代码##

回答by Mike Chirico

If you want to extract substrings from a String, not just the position, (but the actual String including emojis). Then, the following maybe a simpler solution.

如果您想从字符串中提取子字符串,不仅是位置,(而是包含表情符号的实际字符串)。那么,以下可能是一个更简单的解决方案。

##代码##

Example Usage:

示例用法:

##代码##

Will return the following:

将返回以下内容:

##代码##

Note using "\w+" may produce an unexpected ""

注意使用 "\w+" 可能会产生意外的 ""

##代码##

Will return this String array

将返回这个字符串数组

##代码##

回答by Rob Mecham

I found that the accepted answer's solution unfortunately does not compile on Swift 3 for Linux. Here's a modified version, then, that does:

不幸的是,我发现接受的答案的解决方案无法在 Swift 3 for Linux 上编译。这是一个修改后的版本,然后:

##代码##

The main differences are:

主要区别是:

  1. Swift on Linux seems to require dropping the NSprefix on Foundation objects for which there is no Swift-native equivalent. (See Swift evolution proposal #86.)

  2. Swift on Linux also requires specifying the optionsarguments for both the RegularExpressioninitialization and the matchesmethod.

  3. For some reason, coercing a Stringinto an NSStringdoesn't work in Swift on Linux but initializing a new NSStringwith a Stringas the source does work.

  1. Linux 上的 Swift 似乎需要删除NSFoundation 对象上没有 Swift 原生等效项的前缀。(参见Swift 进化提案 #86。)

  2. Linux 上的 Swift 还需要optionsRegularExpression初始化和matches方法指定参数。

  3. 出于某种原因,将 a 强制String转换为 anNSString在 Linux 上的 Swift 中不起作用,但在源代码中NSString使用 a初始化 newString确实有效。

This version also works with Swift 3 on macOS / Xcode with the sole exception that you must use the name NSRegularExpressioninstead of RegularExpression.

此版本也适用于 macOS / Xcode 上的 Swift 3,唯一的例外是您必须使用名称NSRegularExpression而不是RegularExpression.

回答by OliverD

@p4bloch if you want to capture results from a series of capture parentheses, then you need to use the rangeAtIndex(index)method of NSTextCheckingResult, instead of range. Here's @MartinR 's method for Swift2 from above, adapted for capture parentheses. In the array that is returned, the first result [0]is the entire capture, and then individual capture groups begin from [1]. I commented out the mapoperation (so it's easier to see what I changed) and replaced it with nested loops.

@p4bloch 如果要从一系列捕获括号中捕获结果,则需要使用, 而不是的rangeAtIndex(index)方法。这是@MartinR 上面用于 Swift2 的方法,适用于捕获括号。在返回的数组中,第一个结果是整个捕获,然后各个捕获组从 开始。我注释掉了操作(这样更容易看到我更改的内容)并用嵌套循环替换它。NSTextCheckingResultrange[0][1]map

##代码##

An example use case might be, say you want to split a string of title yeareg "Finding Dory 2016" you could do this:

一个示例用例可能是,假设您想拆分一个字符串,title year例如“Finding Dory 2016”,您可以这样做:

##代码##

回答by valexa

Most of the solutions above only give the full match as a result ignoring the capture groups e.g.: ^\d+\s+(\d+)

上面的大多数解决方案只给出完全匹配,因此忽略了捕获组,例如:^\d+\s+(\d+)

To get the capture group matches as expected you need something like (Swift4) :

要按预期获得捕获组匹配,您需要类似 (Swift4) 的内容:

##代码##

回答by Jorge Osorio

This is a very simple solution that returns an array of string with the matches

这是一个非常简单的解决方案,它返回一个包含匹配项的字符串数组

Swift 3.

斯威夫特 3.

##代码##

回答by Dalorzo

This is how I did it, I hope it brings a new perspective how this works on Swift.

我就是这样做的,我希望它能带来一个新的视角,它是如何在 Swift 上工作的。

In this example below I will get the any string between []

在下面的这个例子中,我将得到之间的任何字符串 []

##代码##

回答by shiami

Swift 4 without NSString.

没有 NSString 的 Swift 4。

##代码##

回答by Ken Mueller

The fastest way to return all matches and capture groups in Swift 5

在 Swift 5 中返回所有匹配项和捕获组的最快方法

##代码##

Returns a 2-dimentional array of strings:

返回一个二维字符串数组:

##代码##

returns...

返回...

##代码##