Всем привет! В этой статье мы разберем как работать с NSRegularExpression и NSDataDetector,
всех неравнодушных приглашают под кат.
Регулярное выражение — строка или последовательность символов, которые задают шаблон. С помощью которого можно делать очень гибкие поисковые выборки в тексте.
Допустим у нас имеется некий текст в строке и наша задача найти в нем все перечисленные 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-х символов

Только что мы составили полноценное регулярное выражение.
Неполный справочник зарезервированных символов для РВ:
| Символ | Значение |
|---|---|
| ^ | начало проверяемой строки |
| $ | конец проверяемой строки |
| . | любой символ |
| | | логическое ИЛИ |
| ? | предшествующий символ или группа символов является необязательными |
| + | один или несколько экземпляров предшествующего элемента |
| * | любое количество экземпляров элемента (в том числе и нулевое |
| \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)$" — тут две группы |
Всем кто дочитал до конца и поставил палец вверх, буду очень признателен.

