1

I have the following code (in Swift 1.2), which is heavily inspired by this tutorial on regex expressions (in particular by the function listGroups from the there given playground):

func groupMatch(pattern: String, string: String, groupIndex: Int) -> String? {
    let regex = NSRegularExpression(pattern: pattern, options: nil, error: nil)
    let range = NSMakeRange(0,count(string))
    let match: NSTextCheckingResult? = regex?.firstMatchInString(string, options: nil, range: range)
    let substring: String? = (string as NSString).substringWithRange(match!.rangeAtIndex(groupIndex))
    if groupIndex < match!.numberOfRanges {
        return substring
    } else {
        return nil
    }
}

The idea is that given a pattern, a string and a (positive) integer n the function returns the first substring matched by the n-th group, if it exists. For example,

let pat = "aa\\s*(\\d+)\\.(\\d\\d)"
let str = "aa     1234.56  aa 7.89"

let grp1cap = groupMatch(pat, str, 1) // yields "1234"
let grp2cap = groupMatch(pat, str, 2) // yields "56"

So far so good. However, the definition of groupMatch does not do what I expected. The following line

let grp3cap = groupMatch(pat, str, 3)

does not seem to evaluate to nil. I would like test whether there is a value or not, e.g. like so

func test(pat: String, str: String, idx: Int) -> String {
    if let cap = groupMatch(pat, str, idx) {
        return cap
    } else {
        return ("no capture")
    }
}

but test(pat, str, 3) does not return the "no capture" string. In fact, it returns nothing at all.

What is wrong with the above definition of groupMatch? How do I get the intended behaviour?

2 Answers2

0

You tried to initialize a substring by accessing a non-existent group. Thus, there is an error before the if condition is executed. Return the substring inside if:

func groupMatch(pattern: String, string: String, groupIndex: Int) -> String? {
    let regex = NSRegularExpression(pattern: pattern, options: nil, error: nil)
    let range = NSMakeRange(0,count(string))
    let match: NSTextCheckingResult? = regex?.firstMatchInString(string, options: nil, range: range)

    if groupIndex < match!.numberOfRanges { // RIGHT BELOW \/
        return (string as NSString).substringWithRange(match!.rangeAtIndex(groupIndex))
    } else {
        return nil
    }
}

Then, println(grp3cap) will print nil and println(test(pat, str, 3)) will print no capture.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
0

In addition to stribizhev's answer: the modified function groupMatch still does not do what the title of the post suggests: groupMatch gives an error in case there is no match at all. If we also want to return nil in that case we can do:

func groupMatch(pattern: String, string: String, groupIndex: Int) -> String? {
    let regex = NSRegularExpression(pattern: pattern, options: nil, error: nil)
    let range = NSMakeRange(0,count(string))
    if let match = regex?.firstMatchInString(string, options: nil, range: range) where groupIndex < match.numberOfRanges {
        return (string as NSString).substringWithRange(match.rangeAtIndex(groupIndex))
    } else {
        return nil
    }
}

Now, for example, also test(pat,"",0) yields the desired "no capture".

NB: The elegant optional unwrapping with the where clause I found in the answer to this question.

Community
  • 1
  • 1