Named capture groups with NSRegularExpression

*爱你&永不变心* 提交于 2019-11-30 04:01:33

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);
}

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).

Since iOS 11 named capture groups are supported. See my answer here https://stackoverflow.com/a/47794474/1696733

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!