【问题标题】:Named capture groups with NSRegularExpression使用 NSRegularExpression 命名的捕获组
【发布时间】:2014-09-08 23:59:35
【问题描述】:

NSRegularExpression 是否支持命名捕获组? the documentation 看起来不像,但我想在探索替代解决方案之前检查一下。

【问题讨论】:

    标签: objective-c regex cocoa nsregularexpression


    【解决方案1】:

    iOS 不支持命名分组,你可以做的就是使用Enum

    枚举:

    typedef enum
    {
        kDayGroup = 1,
        kMonthGroup,
        kYearGroup
    } RegexDateGroupsSequence;
    

    示例代码:

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

    【讨论】:

    • 有趣,不知道。
    • 谢谢,这是一个很酷的方法。不过,为了完整起见,我想确保这也适用于 OS X(我目前正在使用它) - 你知道吗?
    • iOS 和 OSX 都使用 NSRegularExpression 类,所以我相信我们也不适合在 OSX 上使用此功能。为了您的方便,我在 iOS 和 OSX 上都尝试了这个线程,除非删除了命名组(?<address>?<params>):stackoverflow.com/questions/8470613/…。希望对您有所帮助!
    【解决方案2】:

    iOS 11 使用 -[NSTextCheckingResult rangeWithName:] API 引入了命名捕获支持。

    要获取命名捕获及其关联值的字典,您可以使用此扩展(用 Swift 编写,但可以从 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
        }
    }
    

    从 Objective-C 调用:

    (lldb) po [@"San Francisco, CA" dictionaryByMatchingRegex:@"^(?<city>.+), (?<state>[A-Z]{2})$"];
    {
        city = "San Francisco";
        state = CA;
    }
    

    代码说明:函数首先需要找出命名捕获的列表。不幸的是,Apple 没有为此发布 API (rdar://36612942)。

    【讨论】:

      【解决方案3】:

      自 iOS 11 起支持命名捕获组。在这里查看我的答案https://stackoverflow.com/a/47794474/1696733

      【讨论】:

        【解决方案4】:

        从 iOS 11 开始可以,我使用NSTextCheckingResult 的这个扩展来获取命名组的值:

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

        用法:

        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
        

        【讨论】:

          猜你喜欢
          • 2016-06-20
          • 1970-01-01
          • 2011-06-12
          • 1970-01-01
          • 2011-10-12
          • 2014-06-26
          • 2015-10-08
          • 2015-10-03
          • 2014-09-11
          相关资源
          最近更新 更多