Named capture groups with NSRegularExpression

后端 未结 4 2037
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-29 00:05

Does NSRegularExpression support named capture groups? It doesn\'t look like it from the documentation, but I wanted to check before I explore alternative solut

相关标签:
4条回答
  • 2020-12-29 00:47

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

    0 讨论(0)
  • 2020-12-29 00:47

    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
    
    0 讨论(0)
  • 2020-12-29 00:49

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

    0 讨论(0)
  • 2020-12-29 00:50

    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);
    }
    
    0 讨论(0)
提交回复
热议问题