Swift extract regex matches

前端 未结 11 1928
无人及你
无人及你 2020-11-21 23:44

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

So I\'m looking for something like this:

func matchesForRegexInText(regex: St         


        
相关标签:
11条回答
  • 2020-11-22 00:31

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

    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: $0).location == NSNotFound ? "" : nsString.substring(with: match.range(at: $0)) }
            } ?? []
        }
    }
    

    Returns a 2-dimentional array of strings:

    "prefix12suffix fix1su".match("fix([0-9]+)su")
    

    returns...

    [["fix12su", "12"], ["fix1su", "1"]]
    
    // First element of sub-array is the match
    // All subsequent elements are the capture groups
    
    0 讨论(0)
  • 2020-11-22 00:31

    Big thanks to Lars Blumberg his answer for capturing groups and full matches with Swift 4, which helped me out a lot. I also made an addition to it for the people who do want an error.localizedDescription response when their regex is invalid:

    extension String {
        func matchingStrings(regex: String) -> [[String]] {
            do {
                let regex = try NSRegularExpression(pattern: regex)
                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: $0).location != NSNotFound
                            ? nsString.substring(with: result.range(at: $0))
                            : ""
                    }
                }
            } catch let error {
                print("invalid regex: \(error.localizedDescription)")
                return []
            }
        }
    }
    

    For me having the localizedDescription as error helped understand what went wrong with escaping, since it's displays which final regex swift tries to implement.

    0 讨论(0)
  • 2020-11-22 00:32

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

    Swift 3.

    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: $0.range)
            }
        }
    
    0 讨论(0)
  • 2020-11-22 00:33

    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 groups for each match (see examples below)
    • Instead of returning an empty array, this solution supports optional matches
    • Avoids do/catch by not printing to the console and makes use of the guard construct
    • Adds matchingStrings as an extension to String

    Swift 4.2

    //: 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: $0).location != NSNotFound
                        ? nsString.substring(with: result.range(at: $0))
                        : ""
                }
            }
        }
    }
    
    "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")
    

    Swift 3

    //: 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($0).location != NSNotFound
                        ? nsString.substring(with: result.rangeAt($0))
                        : ""
                }
            }
        }
    }
    
    "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")
    

    Swift 2

    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($0).location != NSNotFound
                        ? nsString.substringWithRange(result.rangeAtIndex($0))
                        : ""
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 00:41

    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:

    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: $0.range) }
        } catch let error {
            print("invalid regex: \(error.localizedDescription)")
            return []
        }
    }
    

    The main differences are:

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

    2. Swift on Linux also requires specifying the options arguments for both the RegularExpression initialization and the matches method.

    3. For some reason, coercing a String into an NSString doesn't work in Swift on Linux but initializing a new NSString with a String as the source does work.

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

    0 讨论(0)
提交回复
热议问题