使用NSRegularExpression命名捕获组

8
< p > NSRegularExpression 支持命名捕获组吗?从文档上看似乎不支持,但在我探索替代方案之前,我想确认一下。

4个回答

10

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

有趣,我不知道那个。 - Joe Habadas
谢谢,这是一个很酷的方法。不过为了完整起见,我想确保这也适用于我目前正在使用的OS X操作系统 - 你知道吗? - Dov
iOS和OSX都使用NSRegularExpression类,因此我认为我们也可以在OSX上使用这个功能。为了方便起见,我在iOS和OSX上都尝试了这个线程,除非删除命名组(?<address>?<params>),否则该模式将失败:http://stackoverflow.com/questions/8470613/unable-to-extract-information-using-nsregularexpression。希望这有所帮助! - NeverHopeless

8

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

代码解释:该函数首先需要找到命名捕获列表。不幸的是,苹果没有公开 API(rdar://36612942)。


1

0

从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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接