Почему NSRegularExpression не учитывает группы захвата во всех случаях?

Основная проблема: ObjC может сказать мне, что было шесть совпадений, когда мой шаблон @"\\b(\\S+)\\b", но когда мой шаблон @"A b (c) or (d)", он сообщает только об одном совпадении, "c".

Решение

Вот функция, которая возвращает группы захвата в виде NSArray. Я новичок в Objective C, поэтому я подозреваю, что есть лучшие способы выполнить неуклюжую работу, чем создание изменяемого массива и назначение его в конце NSArray.

- (NSArray *)regexWithResults:(NSString *)haystack pattern:(NSString *)strPattern
{
    NSArray *ar;
    ar = [[NSArray alloc] init];
    NSError *error = NULL;
    NSArray *arTextCheckingResults;
    NSMutableArray *arMutable = [[NSMutableArray alloc] init];
    NSRegularExpression *regex = [NSRegularExpression
        regularExpressionWithPattern:strPattern
        options:NSRegularExpressionSearch error:&error];

    arTextCheckingResults = [regex matchesInString:haystack
        options:0
        range:NSMakeRange(0, [haystack length])];

    for (NSTextCheckingResult *ntcr in arTextCheckingResults) {
        int captureIndex;
        for (captureIndex = 1; captureIndex < ntcr.numberOfRanges; captureIndex++) {
            NSString * capture = [haystack substringWithRange:[ntcr rangeAtIndex:captureIndex]];
            //NSLog(@"Found '%@'", capture);
            [arMutable addObject:capture];
        }
    }

    ar = arMutable;
    return ar;
}

Проблема

Я привык использовать круглые скобки для сопоставления групп захвата в Perl следующим образом:

#!/usr/bin/perl -w
use strict;

my $str = "This sentence has words in it.";
if(my ($what, $inner) = ($str =~ /This (\S+) has (\S+) in it/)) {
    print "That $what had '$inner' in it.\n";
}

Этот код будет производить:

    That sentence had 'words' in it.

Но в Objective C с NSRegularExpression мы получаем другие результаты. Пример функции:

- (void)regexTest:(NSString *)haystack pattern:(NSString *)strPattern
{
    NSError *error = NULL;
    NSArray *arTextCheckingResults;

    NSRegularExpression *regex = [NSRegularExpression
                                  regularExpressionWithPattern:strPattern
                                  options:NSRegularExpressionSearch
                                  error:&error];

    NSUInteger numberOfMatches = [regex numberOfMatchesInString:haystack options:0 range:NSMakeRange(0, [haystack length])];

    NSLog(@"Pattern: '%@'", strPattern);
    NSLog(@"Search text: '%@'", haystack);
    NSLog(@"Number of matches: %lu", numberOfMatches);

    arTextCheckingResults = [regex matchesInString:haystack options:0 range:NSMakeRange(0, [haystack length])];

    for (NSTextCheckingResult *ntcr in arTextCheckingResults) {
        NSString *match = [haystack substringWithRange:[ntcr rangeAtIndex:1]];
        NSLog(@"Found string '%@'", match);
    }
}

Вызовы этой тестовой функции, и результаты показывают, что она способна подсчитать количество слов в строке:

NSString *searchText = @"This sentence has words in it.";
[myClass regexTest:searchText pattern:@"\\b(\\S+)\\b"];
    Pattern: '\b(\S+)\b'
    Search text: 'This sentence has words in it.'
    Number of matches: 6
    Found string 'This'
    Found string 'sentence'
    Found string 'has'
    Found string 'words'
    Found string 'in'
    Found string 'it'

Но что, если группы захвата указаны явно, вот так?

[myClass regexTest:searchText pattern:@".*This (sentence) has (words) in it.*"];

Результат:

    Pattern: '.*This (sentence) has (words) in it.*'
    Search text: 'This sentence has words in it.'
    Number of matches: 1
    Found string 'sentence'

То же, что и выше, но с \S+ вместо слов:

[myClass regexTest:searchText pattern:@".*This (\\S+) has (\\S+) in it.*"];

Результат:

    Pattern: '.*This (\S+) has (\S+) in it.*'
    Search text: 'This sentence has words in it.'
    Number of matches: 1
    Found string 'sentence'

Как насчет подстановочного знака посередине?

[myClass regexTest:searchText pattern:@"^This (\\S+) .* (\\S+) in it.$"];

Результат:

    Pattern: '^This (\S+) .* (\S+) in it.$'
    Search text: 'This sentence has words in it.'
    Number of matches: 1
    Found string 'sentence'

Ссылки: NSRegularExpression NSTextCheckingResult Параметры сопоставления NSRegularExpression


person JD.    schedule 29.09.2011    source источник
comment
Это также может быть связано с тем, как я использую NSTextCheckingResult.   -  person JD.    schedule 29.09.2011


Ответы (2)


Я думаю, если вы измените

// returns the range which matched the pattern
NSString *match = [haystack substringWithRange:ntcr.range];

to

// returns the range of the first capture
NSString *match = [haystack substringWithRange:[ntcr rangeAtIndex:1]];

Вы получите ожидаемый результат для шаблонов, содержащих один захват.

См. страницу документа для NSTextCheckingResult:rangeAtIndex:

Результат должен иметь хотя бы один диапазон, но может быть и больше (например, для представления групп захвата регулярных выражений).

Передача rangeAtIndex: значение 0 всегда возвращает значение свойства диапазона. Дополнительные диапазоны, если они есть, будут иметь индексы от 1 до numberOfRanges-1.

person Dan Treiman    schedule 29.09.2011

Измените NSTextCheckingResult:

- (void)regexTest:(NSString *)haystack pattern:(NSString *)strPattern
{
    NSError *error = NULL;
    NSArray *arTextCheckingResults;

    NSRegularExpression *regex = [NSRegularExpression
                                  regularExpressionWithPattern:strPattern
                                  options:NSRegularExpressionSearch
                                  error:&error];
    NSRange stringRange = NSMakeRange(0, [haystack length]);
    NSUInteger numberOfMatches = [regex numberOfMatchesInString:haystack
                                                        options:0 range:stringRange];

    NSLog(@"Number of matches for '%@' in '%@': %u", strPattern, haystack, numberOfMatches);

    arTextCheckingResults = [regex matchesInString:haystack options:NSRegularExpressionCaseInsensitive range:stringRange];

    for (NSTextCheckingResult *ntcr in arTextCheckingResults) {
        NSRange matchRange = [ntcr rangeAtIndex:1];
        NSString *match = [haystack substringWithRange:matchRange];
        NSLog(@"Found string '%@'", match);
    }
}

Вывод NSLog:
Найдена строка 'words'

person zaph    schedule 29.09.2011
comment
Зачем использовать NSRegularExpressionSearch в следующих строках? NSRegularExpression *regex = [NSRegularExpression RegularExpressionWithPattern:strPattern options:NSRegularExpressionSearch error:&error]; Влияет ли это на вызов следующего? arTextCheckingResults = [регулярное выражение matchInString: параметры стога сена: диапазон NRegularExpressionCaseInsensitive: stringRange]; - person redwud; 31.12.2013
comment
regex является регулярным выражением и используется для создания arTextCheckingResults. - person zaph; 31.12.2013
comment
да, но NSRegularExpressionSearch не является типом NSRegularExpressionOptions, и ни одно из перечислений в NSRegularExpressionOptions не является NSRegularExpressionSearch. - person redwud; 31.12.2013