Почему NSRegularExpression не соблюдает группы захвата во всех случаях? - PullRequest
3 голосов
/ 29 сентября 2011

Основная проблема: 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.

Но в Цели 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

Ответы [ 2 ]

7 голосов
/ 29 сентября 2011

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

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

до

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

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

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

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

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

1 голос
/ 29 сентября 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'

...