optional capture groups with NSRegularExpressions in swift
Asked Answered
P

1

0

I want to have multiple capture groups that can be optional and I want to access the strings they correspond to.

Something that looks/works like this:

let text1 = "something with foo and bar"
let text2 = "something with just bar"
let regex = NSRegularExpression(pattern: "(foo)? (bar)")

for (first?, second) in regex.matches(in:text1) {
   print(first) // foo
   print(second) // bar
}

for (first?, second) in regex.matches(in:text2) {
   print(first) // nil
   print(second) // bar
}
Phenocryst answered 3/12, 2016 at 18:26 Comment(0)
C
6

Retrieving captured subtext with NSRegularExpression is not so easy.

First of all, the result of matches(in:range:) is [NSTextCheckingResult], and each NSTextCheckingResult does not match to tuple like (first?, second).

If you want to retrieve captured subtext, you need to get the range from the NSTextCheckingResult with rangeAt(_:) method. rangeAt(0) represents the range matching the whole pattern, rangeAt(1) for the first capture, rangeAt(2) for the second, and so on.

And rangeAt(_:) returns an NSRange, not Swift Range. The content (location and length) is based on the UTF-16 representation of NSString.

And this is the most important part for your purpose, rangeAt(_:) returns NSRange(location: NSNotFound, length: 0) for each missing capture.

So, you may need to write something like this:

let text1 = "something with foo and bar"
let text2 = "something with just bar"
let regex = try! NSRegularExpression(pattern: "(?:(foo).*)?(bar)") //please find a better example...

for match in regex.matches(in: text1, range: NSRange(0..<text1.utf16.count)) {
    let firstRange = match.rangeAt(1)
    let secondRange = match.rangeAt(2)
    let first = firstRange.location != NSNotFound ? (text1 as NSString).substring(with: firstRange) : nil
    let second = (text1 as NSString).substring(with: secondRange)
    print(first) // Optioonal("foo")
    print(second) // bar
}

for match in regex.matches(in: text2, range: NSRange(0..<text2.utf16.count)) {
    let firstRange = match.rangeAt(1)
    let secondRange = match.rangeAt(2)
    let first = firstRange.location != NSNotFound ? (text2 as NSString).substring(with: firstRange) : nil
    let second = (text2 as NSString).substring(with: secondRange)
    print(first) // nil
    print(second) // bar
}
Compte answered 3/12, 2016 at 21:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.