Swift Regular Expressions Capture Group Difficulty

Viewed 59

Trying to work out a regular expression. The output is sporadic. Each capture group has at least one nil value in a varying place for each of my three named capture groups and sometimes the capture groups are out of order. Have checked SO for a solution but no luck so far, so thought I'd ask.

Basically, my text file contains a bunch of hex's that combine to form gradients. The relevant stuff I need to scrape within the file looks like this, repeating periodically throughout our file:

002 Tropic Thunder 


#a18cd2|#fbc4eb

First, I have been reading in the rtf file:

if let rtfPath = Bundle.main.url(forResource: "mytextfile", withExtension: "rtf") {
    do {
        let attributedStringWithRtf: NSAttributedString = try NSAttributedString(url: rtfPath, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.rtf], documentAttributes: nil)
        self.testString = attributedStringWithRtf
    } catch let error {
         print("Got an error \(error)")
    }
}
let myString = self.testString?.string

Next, I create the pattern, call my function, and print the result:

do {
    pattern = try NSRegularExpression(pattern: "[0-9]{3}\\s*(?<gradientName>[a-zA-Z]{2,11}\\s[a-zA-Z]{2,11})\\s*(?<hexCode1>#[0-9a-fA-F]{6})|(?<hexCode2>#[0-9a-fA-F]{6})")
} catch {
    print(error)
}
        
let result = namedCaptureGroupsInMatches(of: pattern!, against: myString!)
       
print(result)

My function looks like this:

func namedCaptureGroupsInMatches(of regularExpression:NSRegularExpression, against string:String) -> [[String:Substring?]] {
    let names = namedCaptureGroups(inRegularExpression: regularExpression)

    let ms = regularExpression.matches(in: string, options: [],
                                         range:NSRange(string.startIndex..<string.endIndex,
                                                       in:string))
    return ms.map({ (tcr:NSTextCheckingResult) -> [String:Substring?] in
        let keyvalues = names.map({ (name:String) -> (String,Substring?) in
            let captureGroupRange = tcr.range(withName: name)
            if captureGroupRange.location == NSNotFound {
                return (name,nil)
            } else {
                return (name,string[Range(captureGroupRange, in: string)!])
            }
        })

        return Dictionary(uniqueKeysWithValues: keyvalues)
    })
}
func namedCaptureGroups(inRegularExpression regularExpression:NSRegularExpression) -> [String]
{
  let regexString = regularExpression.pattern
  let captureGroupNames = try! NSRegularExpression(pattern: "\\(\\?\\<(\\w+)\\>", options: [])
  let groupNameMatches = captureGroupNames.matches(in: regexString, options: [],
                                      range: NSRange(regexString.startIndex..<regexString.endIndex,
                                                     in:regexString))
  let names = groupNameMatches.map { (textCheckingResult) -> String in
    return (regexString as NSString).substring(with: textCheckingResult.range(at: 1))
  }
  return names
}

Any ideas?

1 Answers

You problem is that you did not escape the | so it was interpreted as alternative (same as "or") and your full match would not include the second hexa. You just need to escape it to match it literally:

let pattern = #"[0-9]{3}\s*(?<gradientName>[a-zA-Z]{2,11}\s[a-zA-Z]{2,11})\s*(?<hexCode1>#[0-9a-fA-F]{6})\|(?<hexCode2>#[0-9a-fA-F]{6})"#
do {
    let regex = try NSRegularExpression(pattern: pattern)
    let result = namedCaptureGroupsInMatches(of: regex, against: myString)     
    print(result)
} catch {
    print(error)
}
Related