Работа с регулярными выражениями в iOS 10
Всем привет! В этой статье мы разберем как работать с 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)$" — тут две группы |
Всем кто дочитал до конца и поставил палец вверх, буду очень признателен.
Проект можно найти по этой ссылке: здесь
Автор: hack_developer