NSRegularExpression и NSDataDetector — Быстрый старт

в 8:49, , рубрики: iOS, objective-c, поиск данных, разработка под iOS, Регулярные выражения

Работа с регулярными выражениями в iOS 10

Всем привет! В этой статье мы разберем как работать с NSRegularExpression и NSDataDetector,
всех неравнодушных приглашают под кат.

some_text


Регулярное выражение — строка или последовательность символов, которые задают шаблон. С помощью которого можно делать очень гибкие поисковые выборки в тексте.

Допустим у нас имеется некий текст в строке и наша задача найти в нем все перечисленные email-адреса. Самый лучший инструмент для этой задачи это регулярные выражения.

Пишем Р.В. (Регулярное Выражение) для проверки email. Для начала нужно описать базовые условия всех возможных комбинаций видов эл.адресов. В самом минимальном варианте, он может иметь следующий вид: a@b.io

  • от 1 символа на имя ящика — до 64-х символов
  • от 1 зарезервированных символ «собака»
  • от 1 символа имени ресурса — до 64-х символов
  • от 2 символа домена — до 64-х символов

Начинаем составлять РВ поэтапно:

@"([a-z0-9])"
— говорит, что имя ящика может содержать любую букву и любую цифру


@"([a-z0-9]){1,64}"
— говорит, что группа этих символов может быть длиной от 1 до 64-х символов


@"([a-z0-9!#$%&'*+-/=?^_`{|}~]){1,64}"
— говорит, что можно указать еще и эти символы
@"([a-z0-9!#$%&'*+-/=?^_`{|}~]){1,64}@"
— говорит, что собака на данном месте обязательна


@"([a-z0-9!#$%&'*+-/=?^_`{|}~]){1,64}@([a-z0-9!#$%&'*+-/=?^_`{|}~]){1,64}"
— Добавляем все те же символы, только после @


@"([a-z0-9!#$%&'*+-/=?^_`{|}~]){1,64}@([a-z0-9!#$%&'*+-/=?^_`{|}~]){1,64}\."
— говорит, что символ точка на этом месте обязательна


@"([a-z0-9!#$%&'*+-/=?^_`{|}~]){1,64}@([a-z0-9!#$%&'*+-/=?^_`{|}~]){1,64}\.([a-z0-9]){2,64}"
— говорит, что имя домена может содержать буквы и цифры общей суммы символов от 2 до 64-х символов


some_text

Только что мы составили полноценное регулярное выражение.

Неполный справочник зарезервированных символов для РВ:


Символ Значение
^ начало проверяемой строки
$ конец проверяемой строки
. любой символ
| логическое ИЛИ
? предшествующий символ или группа символов является необязательными
+ один или несколько экземпляров предшествующего элемента
* любое количество экземпляров элемента (в том числе и нулевое
d цифровой символ
D не циф. символ
s пробельный символ
S не пробельный символ
w соответствует любой букве или цифре; эквивалент [a-zA-Z0-9_]
W наоборот эквивалент [^a-zA-Z0-9_], все символы кроме этих
Квантификаторы
{n} ровно n раз
{m,n} включительно от M до N
{m,} не менее m раз
{,n} не более n раз
() создание группы
[] в таких скобках говорим, «любой символ из этих, но только один

Создание NSRegularExpression

self.mainText = @"There’s also +39(081)-552-1488 a “Bookmark” button that allows the user to highlight any date, time or location in the text. For simplicity’s sake, www.ok.ru you won’t 
+39(081)552 2080 not cover every possible format of date, time and 2693485 location strings that can appear in your text. You’ll implement the https://github.com bookmarking functionality +249-54-85 at the very end +39 333 3333333 of the tutorial. vk.com Your first step to getting the search 
functionality working is to turn standard strings representing regular expressions into http://app.com NSRegularExpression objects.";

NSString* pattern = @"\b(in)|(or)\b";        // Хотим искать слова "in" или "or" 
NSRegularExpressionOptions regexOptions = NSRegularExpressionCaseInsensitive; // Поиск вне зависимости от регистра
NSError* error = NULL;                                                        
// Само создание регулярного выражения  
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern
                                options:regexOptions 
                                   error:&error];
if (error){ 
  NSLog(@"Ошибка при создании Regular Expression"); // Если в pattern были внесены корректные данные, тогда это сообщение не появиться
} 

Справедливости ради должен отметить, что при создании паттерна, в строке мы должны писать непросто w,(или любую другую букву или символ который зарезервирован)
а писать backslash два раза что бы экранировать NSString. \w


Манипуляции с экземпляром NSRegularExpression

Общее количество всех найденных совпадений

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

Возвращает range первого совпадения

    NSRange rangeOfFirstMatch = [regex rangeOfFirstMatchInString:_mainText
                                                         options:0
                                                           range:NSMakeRange(0, [_mainText length])];
    
    if (!NSEqualRanges(rangeOfFirstMatch, NSMakeRange(NSNotFound, 0))) {
        NSString *substringForFirstMatch = [_mainText substringWithRange:rangeOfFirstMatch];
    }

Получаем массив всех найденных совпадений

    NSArray *matches = [regex matchesInString:_mainText
                                      options:0
                                        range:NSMakeRange(0, [_mainText length])];
    for (NSTextCheckingResult *match in matches) {

        //=== Через проперти resultType, можно проверить
        //    к какому типу относиться найденый матч
        
        if (match.resultType == NSTextCheckingTypeQuote) NSLog(@"Цитата!");
        
        NSRange matchRange      = [match range];
        NSRange firstHalfRange  = [match rangeAtIndex:1];
        NSRange secondHalfRange = [match rangeAtIndex:2];
    }  

Получаем первое совпадение из общего количества

    NSTextCheckingResult *match = [regex firstMatchInString:_mainText
                                                    options:0
                                                      range:NSMakeRange(0, [_mainText length])];
    if (match) {
        NSRange matchRange = [match range];
        NSRange firstHalfRange = [match rangeAtIndex:1];
        NSRange secondHalfRange = [match rangeAtIndex:2];
    }

Проходим итератором блоком по каждому совпадению

    __block NSUInteger count = 0;
    [regex enumerateMatchesInString:_mainText options:0 range:NSMakeRange(0, [_mainText length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
        
        NSRange matchRange = [match range];
        NSRange firstHalfRange = [match rangeAtIndex:1];
        NSRange secondHalfRange = [match rangeAtIndex:2];
        if (++count >= 100) *stop = YES;
   
    }];

Нахождение нашего паттерна в тексте и вставляем на его место слово Sieg!!!

    NSString *modifiedString = [regex stringByReplacingMatchesInString:_mainText
                                                               options:0
                                                                 range:NSMakeRange(0, [_mainText length])
                                                          withTemplate:@"Sieg!!!"];    

Обзор класса NSDataDetector

NSDataDetector — это подкласс NSRegularExpression, сделанный для удобного поиска (ссылок, номеров телефонов, даты и т.д.). То есть фактический этот класс у себя под капотом содержит универсальные регулярные выражения для поиска вышеперечисленного.

Также NSDataDetector может вызывать все методы NSRegularExpression, также искать firstMatch/matches всего текста и т.д.

Манипуляции с экземпляром NSDataDetector


Создание экземпляра NSDataDectector

  NSError* error1 = nil;
  NSDataDetector* detector=[NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink |
                                                                 NSTextCheckingTypePhoneNumber
                                                           error:&error];
                                                               
   /* Типы данных, того чего можно искать
     
            NSTextCheckingTypeOrthography
            NSTextCheckingTypeSpelling
            NSTextCheckingTypeGrammar
            NSTextCheckingTypeDate
            NSTextCheckingTypeAddress
            NSTextCheckingTypeLink
            NSTextCheckingTypeQuote
            NSTextCheckingTypeDash
            NSTextCheckingTypeReplacement
            NSTextCheckingTypeCorrection
            NSTextCheckingTypeRegularExpression
            NSTextCheckingTypePhoneNumber
            NSTextCheckingTypeTransitInformation
    */

Количество найденных совпадений в тексте

NSUInteger numberOfMatchesFromDetect = [detector numberOfMatchesInString:_mainText
                                                 options:0 
                                                   range:NSMakeRange(0,[_mainText length])];

Все совпадения в одном массиве (он содержит объекты NSTextCheckingResult)

 NSArray* matchesFromDetect =[detector matchesInString:_mainText
                                               options:0 
                                                 range:NSMakeRange(0,[_mainText length])];
   
    // ----- Проходим по каждому совпадению и смотрим что там
    
    for (NSTextCheckingResult* match in matchesFromDetect)
    {
        NSLog(@"------------");
        NSRange matchRange = [match range];
       
        if ([match resultType] == NSTextCheckingTypeLink)
        {
            NSURL* url = [match URL];
            NSLog(@"url = %@",url.absoluteString);
            
        } else if ([match resultType] == NSTextCheckingTypePhoneNumber)
            {
            NSString *phoneNumber = [match phoneNumber];
            NSLog(@"phone = %@",phoneNumber);
            }
    }

Проходим блоком по всем совпадениям

    __block NSUInteger countForDectect = 0;
    
    [detector enumerateMatchesInString:_mainText
                               options:0 
                                 range:NSMakeRange(0, [_mainText length])
                            usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
        
                NSRange matchRange = result.range;
                NSLog(@"In Enumerate = %d, tRange = %@t tText = %@",countForDectect, NSStringFromRange(matchRange), [_mainText substringWithRange:matchRange]);
                countForDectect++;
    }];

Краткая документация свойствам

Проперти экземпляра NSRegularExpression Трактовка
var pattern: String Возвращает паттерн регулярного выражения.
var options: NSRegularExpression.Options Возвращает параметры поиска например: ищем без учета регистра NSRegularExpressionCaseInsensitive.
var numberOfCaptureGroups: Int Возвращает количество групп из паттерна регулярного выражения, например: @»^(ab|bc)(amg|img)$" — тут две группы

Всем кто дочитал до конца и поставил палец вверх, буду очень признателен.
Проект можно найти по этой ссылке: здесь

Автор: hack_developer

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js