如何在Objective C (NSRegularExpression)中编写正则表达式?

52

我在PHP中测试这个正则表达式时它可以工作,但在Objective-C中不能:

(?:www\.)?((?!-)[a-zA-Z0-9-]{2,63}(?<!-))\.?((?:[a-zA-Z0-9]{2,})?(?:\.[a-zA-Z0-9]{2,})?)

我尝试转义转义字符,但这也没有帮助。我应该转义其他字符吗?

这是我在 Objective C 中的代码:

NSMutableString *searchedString = [NSMutableString stringWithString:@"domain-name.tld.tld2"];
NSError* error = nil;

NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"(?:www\\.)?((?!-)[a-zA-Z0-9-]{2,63}(?<!-))\\.?((?:[a-zA-Z0-9]{2,})?(?:\\.[a-zA-Z0-9]{2,})?)" options:0 error:&error];
NSArray* matches = [regex matchesInString:searchedString options:0 range:NSMakeRange(0, [searchedString length])];
for ( NSTextCheckingResult* match in matches )
{
    NSString* matchText = [searchedString substringWithRange:[match range]];
    NSLog(@"match: %@", matchText);
}
-- 更新 --
这个正则表达式(在 PHP 中)返回值为“domain-name”和“tld.tld2”的数组,但在 Objective C 中只得到一个值:“domain-name.tld.tld2”。
-- 更新 2 --
这个正则表达式从字符串中提取“域名”和“TLD”:
- example.com = (example, com) - example.co.uk = (example, co.uk) - -test-example.co.u = (test-example, co) - -test-example.co.uk- = (test-example, co.uk) - -test-example.co.u-k = (test-example, co) - -test-example.co-m = (test-example) - -test-example-.co.uk = (test-example)
它获取有效的域名(不以“-”开头或结尾,长度为2到63个字符),以及最多两个有效的 TLD 部分(至少两个字符长,只包含字母和数字)。

也许这可以帮助?https://dev59.com/AMOdzogBFxS5KdRjikxl - dom
2
我认为Dino正在询问如何将现有的正则表达式转换为ObjectiveC代码。无论正则表达式的功能是什么,都不重要。 - Edi Budimilic
2个回答

84

NSTextCheckingResult可以通过索引获得多个项。

[match rangeAtIndex:0];是完整的匹配。
[match rangeAtIndex:1];(如果存在)是第一个捕获组匹配。
等等。

您可以像这样使用:

NSString *searchedString = @"domain-name.tld.tld2";
NSRange   searchedRange = NSMakeRange(0, [searchedString length]);
NSString *pattern = @"(?:www\\.)?((?!-)[a-zA-Z0-9-]{2,63}(?<!-))\\.?((?:[a-zA-Z0-9]{2,})?(?:\\.[a-zA-Z0-9]{2,})?)";
NSError  *error = nil;

NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern: pattern options:0 error:&error];
NSArray* matches = [regex matchesInString:searchedString options:0 range: searchedRange];
for (NSTextCheckingResult* match in matches) {
    NSString* matchText = [searchedString substringWithRange:[match range]];
    NSLog(@"match: %@", matchText);
    NSRange group1 = [match rangeAtIndex:1];
    NSRange group2 = [match rangeAtIndex:2];
    NSLog(@"group1: %@", [searchedString substringWithRange:group1]);
    NSLog(@"group2: %@", [searchedString substringWithRange:group2]);
}

NSLog输出:

匹配: 域名.tld.tld2
域名
tld.tld2

确保测试匹配范围是有效的。

在这种情况下更简单的写法:

NSString *searchedString = @"domain-name.tld.tld2";
NSRange   searchedRange = NSMakeRange(0, [searchedString length]);
NSString *pattern = @"(?:www\\.)?((?!-)[a-zA-Z0-9-]{2,63}(?<!-))\\.?((?:[a-zA-Z0-9]{2,})?(?:\\.[a-zA-Z0-9]{2,})?)";
NSError  *error = nil;

NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
NSTextCheckingResult *match = [regex firstMatchInString:searchedString options:0 range: searchedRange];
NSLog(@"group1: %@", [searchedString substringWithRange:[match rangeAtIndex:1]]);
NSLog(@"group2: %@", [searchedString substringWithRange:[match rangeAtIndex:2]]);

Swift 3.0:

let searchedString = "domain-name.tld.tld2"
let nsSearchedString = searchedString as NSString
let searchedRange = NSMakeRange(0, searchedString.characters.count)
let pattern = "(?:www\\.)?((?!-)[a-zA-Z0-9-]{2,63}(?<!-))\\.?((?:[a-zA-Z0-9]{2,})?(?:\\.[a-zA-Z0-9]{2,})?)"

do {
    let regex = try NSRegularExpression(pattern:pattern, options: [])
    let matches = regex.matches(in:searchedString, options:[], range:searchedRange)
    for match in matches {
        let matchText = nsSearchedString.substring(with:match.range);
        print("match: \(matchText)");

        let group1 : NSRange = match.rangeAt(1)
        let matchText1 = nsSearchedString.substring(with: group1)
        print("matchText1: \(matchText1)")

        let group2 = match.rangeAt(2)
        let matchText2 = nsSearchedString.substring(with: group2)
        print("matchText2: \(matchText2)")
    }
} catch let error as NSError {
    print(error.localizedDescription)
}

输出结果:

匹配结果: domain-name.tld.tld2
匹配文本1: domain-name
匹配文本2: tld.tld2

更简单的说,在这种情况下:

do {
    let regex = try NSRegularExpression(pattern:pattern, options: [])
    let match = regex.firstMatch(in:searchedString, options:[], range:searchedRange)

    let matchText1 = nsSearchedString.substring(with: match!.rangeAt(1))
    print("matchText1: \(matchText1)")

    let matchText2 = nsSearchedString.substring(with: match!.rangeAt(2))
    print("matchText2: \(matchText2)")

} catch let error as NSError {
    print(error.localizedDescription)
}

打印输出:

匹配文本1:域名
匹配文本2:顶级域名.次级域名


我的上面的代码目前显示了所有匹配的结果(完全匹配、域名、顶级域名),当我在在线测试该正则表达式时,它是有效的。但是在 Objective C 中测试时,它只返回第一个值(完全匹配)。 - budiDino
看起来我在“NSMutableString stringWithString”这部分有问题,因为正则表达式与在线VS Objective C匹配的内容不同。 - budiDino
2
对不起,你是正确的。看起来我没有正确理解如何获取结果。我本质上是一名Web开发人员,而Objective C使我尝试做的每件事情都比在PHP中难至少两倍:) 再次感谢。 - budiDino
2
整个 NSTextCheckingResult 太过复杂,对于正则表达式来说有些过度。它并不明显地包含捕获组。 - zaph
哈哈,我刚刚看到了我在这里的第二条评论。我一直在想“NSRegularExpression regularExpressionWithPattern”,但是复制粘贴了“NSMutableString stringWithString” :) 无论如何,我喜欢这里的社区! - budiDino

17
根据苹果文档,这些字符必须用反斜杠引用才能被视为文字字面量:
* ? + [ ( ) { } ^ $ | \ . /

如果您能解释您想要实现什么,那将有所帮助。您是否有任何测试夹具?


在标准正则表达式意义上?还是要被视为正则表达式运算符? - Adam Waite
我一直在尝试在obj-c中使用正则表达式,但是我无法使\[正常工作。有什么想法吗? - ZuluDeltaNiner
11
您必须先转义反斜杠。\\ - Maciej Swic
我还要补充一点,如果你想搜索 #(作为字面量),NSRegularExpression init 将会失败。我用 \u0023(或者更确切地说是 \u0023)代替了它,这样就可以正常工作了。 - horseshoe7

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