Does NSRegularExpression support named capture groups? It doesn't look like it from the documentation, but I wanted to check before I explore alternative solutions.
            Asked
            
        
        
            Active
            
        
            Viewed 5,362 times
        
    8
            
            
        
        Dov
        
- 15,530
 - 13
 - 76
 - 177
 
4 Answers
10
            
            
        Named grouping is not supported in iOS, all you can do as I see is to make use of Enum:
Enum:
typedef enum
{
    kDayGroup = 1,
    kMonthGroup,
    kYearGroup
} RegexDateGroupsSequence;
Sample Code:
NSString *string = @"07-12-2014";
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(\\d{2})\\-(\\d{2})\\-(\\d{4}|\\d{2})"
                                                                       options:NSRegularExpressionCaseInsensitive
                                                                         error:&error];
NSArray *matches = [regex matchesInString:string
                                  options:0
                                    range:NSMakeRange(0, [string length])];
for (NSTextCheckingResult *match in matches) {
    NSString *day = [string substringWithRange:[match rangeAtIndex:kDayGroup]];
    NSString *month = [string substringWithRange:[match rangeAtIndex:kMonthGroup]];
    NSString *year = [string substringWithRange:[match rangeAtIndex:kYearGroup]];
    NSLog(@"Day: %@, Month: %@, Year: %@", day, month, year);
}
        NeverHopeless
        
- 11,077
 - 4
 - 35
 - 56
 
- 
                    interesting, didn't know about that. – Joe Habadas Jul 18 '14 at 06:25
 - 
                    Thanks, that's a cool approach. For completeness, though, I want to make sure this applies to OS X as well (which I'm working in at the moment) - do you know? – Dov Jul 18 '14 at 18:52
 - 
                    iOS and OSX both uses `NSRegularExpression` class so i believe we are not faciliate to use this feature for OSX as well. For your convenience, i tried this thread on both iOS and OSX and this pattern fails unless the named group (`?` and `?
` ) removed: http://stackoverflow.com/questions/8470613/unable-to-extract-information-using-nsregularexpression. Hope it helps! – NeverHopeless Jul 18 '14 at 22:19 
8
            
            
        iOS 11 introduced named capture support using the -[NSTextCheckingResult rangeWithName:] API.
To get a dictionary of named captures with their associated values you can use this extension (written in Swift, but can be called from Objective C):
@objc extension NSString {
    public func dictionaryByMatching(regex regexString: String) -> [String: String]? {
        let string = self as String
        guard let nameRegex = try? NSRegularExpression(pattern: "\\(\\?\\<(\\w+)\\>", options: []) else {return nil}
        let nameMatches = nameRegex.matches(in: regexString, options: [], range: NSMakeRange(0, regexString.count))
        let names = nameMatches.map { (textCheckingResult) -> String in
            return (regexString as NSString).substring(with: textCheckingResult.range(at: 1))
        }
        guard let regex = try? NSRegularExpression(pattern: regexString, options: []) else {return nil}
        let result = regex.firstMatch(in: string, options: [], range: NSMakeRange(0, string.count))
        var dict = [String: String]()
        for name in names {
            if let range = result?.range(withName: name),
                range.location != NSNotFound
            {
                dict[name] = self.substring(with: range)
            }
        }
        return dict.count > 0 ? dict : nil
    }
}
Call from Objective-C:
(lldb) po [@"San Francisco, CA" dictionaryByMatchingRegex:@"^(?<city>.+), (?<state>[A-Z]{2})$"];
{
    city = "San Francisco";
    state = CA;
}
Code explanation: The function first needs to find out the list of named captures. Unfortunately, Apple didn't publish an API for that (rdar://36612942).
        Ortwin Gentz
        
- 52,648
 - 24
 - 135
 - 213
 
1
            
            
        Since iOS 11 named capture groups are supported. See my answer here https://stackoverflow.com/a/47794474/1696733
        jtmayer
        
- 401
 - 4
 - 13
 
0
            
            
        It is possible from iOS 11, I use this extension of NSTextCheckingResult to get the values of named groups:
extension NSTextCheckingResult {
    func match(withName name: String, in string: String) -> String? {
        let matchRange = range(withName: name)
        guard matchRange.length > 0 else {
            return nil
        }
        let start = string.index(string.startIndex, offsetBy: matchRange.location)
        return String(string[start..<string.index(start, offsetBy: matchRange.length)])
    }
}
Usage:
let re = try! NSRegularExpression(pattern: "(?:(?<hours>\\d+):)(?:(?<minutes>\\d+):)?(?<seconds>\\d+)", options: [])
var str = "40 16:00:00.200000"
let result = re.firstMatch(in: str, options: [], range: NSRange(location: 0, length: str.count))
result?.match(withName: "hours", in: str)  // 16
        juanjo
        
- 3,737
 - 3
 - 39
 - 44