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

#objective-c #regex #nsregularexpression

#objective-c #регулярное выражение #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:amp;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";
}
  

Этот код приведет к:

 В этом предложении были "слова".

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

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

    NSRegularExpression *regex = [NSRegularExpression
                                  regularExpressionWithPattern:strPattern
                                  options:NSRegularExpressionSearch
                                  error:amp;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"];
  
 Шаблон: ' b( S  )  b'
 Текст для поиска: "В этом предложении есть слова".
Количество совпадений: 6
 Найдена строка 'This'
 Найдена строка 'предложение'
 Найденная строка 'имеет'
 Найденная строка "слова"
 Найдена строка 'in'
 Найденная строка 'it'

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

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

Результат:

 Шаблон: '.* В этом (предложении) есть (слова). *'
 Текст поиска: "В этом предложении есть слова".
Количество совпадений: 1
 Найдена строка 'предложение'

То же, что и выше, но с S вместо фактических слов:

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

Результат:

 Шаблон: '.* В этом (S  ) есть (S ) .*'
 Текст поиска: "В этом предложении есть слова".
Количество совпадений: 1
 Найдена строка 'предложение'

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

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

Результат:

 Шаблон: '^Это (S ) .* ( S  ) в нем.$'
 Текст поиска: "В этом предложении есть слова".
Количество совпадений: 1
 Найдена строка 'предложение'

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

Комментарии:

1. Это также может быть связано с тем, как я использую NSTextCheckingResult.

Ответ №1:

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

 // 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.

Ответ №2:

Измените NSTextCheckingResult :

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

    NSRegularExpression *regex = [NSRegularExpression
                                  regularExpressionWithPattern:strPattern
                                  options:NSRegularExpressionSearch
                                  error:amp;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:

Найдена строка «слова»

Комментарии:

1. Зачем использовать NSRegularExpressionSearch в следующих строках? NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:Параметры strPattern:Ошибка поиска NSRegularExpressionSearch:amp;ошибка]; Влияет ли это на вызов следующего? arTextCheckingResults = [регулярное выражение соответствует строке:параметры стога сена:NSRegularExpressionCaseInsensitive диапазон:stringRange];

2. regex является регулярным выражением и используется для создания arTextCheckingResults .

3. да, но NSRegularExpressionSearch не является типом NSRegularExpressionOptions, и ни одно из перечислений inNSRegularExpressionOptions не является NSRegularExpressionSearch .