Приветствую всех, сегодня хотел поговорить о регулярных выражения. Регулярные выражения появились очень давно, но посей день широко используются в программировании. Это на столько эффективный способ обработки текста, что позволяет без труда анализировать большие объемы текста, символов, или заданного шаблона поиска. Для приложений использующих большие тексты, регулярные выражения просто не заменимы!
Основа обработки текста с помощью регулярных выражений — это подсистема обработки регулярных выражений, представленная в платформе .NET Framework объектом System.Text.RegularExpressions.Regex. Минимальный набор сведений, который требуется предоставить подсистеме обработки регулярных выражений для обработки текста, сводится к: созданию шаблона регулярного выражения и применение его к анализируемому тексту.
Методы класса Regex, позволяют определить, встречается ли во входном тексте шаблон регулярного выражения. Для этого можно использовать метод IsMatch. Также
можно извлечь из текста одно или все вхождения, соответствующие шаблону регулярного выражения, путем вызова метода Match или Matches. Первый метод возвращает объект Match, предоставляющий сведения о совпадении в тексте. Второй метод возвращает коллекцию MatchCollection, в которую входят объекты Match для всех совпадений, найденных в проанализированном тексте.
Заменить текст, соответствующий шаблону регулярного выражения, можно путем вызова метода Replace.
IsMatch — метод возвращающий bool. True — в случае, если шаблон соответствует строке или false — в противном случае. Метод IsMatch — сравнивает принимаемую в первом параметре строку с шаблоном.
При проверке вводимых данных рекомендуется начинать регулярные выражения с символа «^» и заканчивать их символом «$». Это гарантирует проверку строки, точно соответствующей заданному шаблону, а не просто содержащей его.
Метасимволы:
Символ | Значение | Пример | Соответствует |
---|---|---|---|
Классы символов | |||
[…] | Любой из символов, указанных в скобках | [a-z] | В исходной строке может быть любой символ английского алфавита в нижнем регистре |
[^…] | Любой из символов, не указанных в скобках | [^0-9] | В исходной строке может быть любой символ кроме цифр |
. | Любой символ, кроме переноса строки \n | Любой единичный символ | |
\w | Буква, цифра или знак нижнего подчёркивания, | «Словесный» символ (все буквы, цифры и подчеркивание (‘_’)) | |
\W | Не: буква, цифра или знак нижнего подчёркивания, | Все, кроме символов, определяемых метасимволом \w | |
\s | Указывает что символом будет пробел | Пустой символ (пробел и табуляция) | |
\S | Любой не пробельный символ из набора Unicode. | Непустой символ (все, кроме символов, определяемых метасимволом \s) | |
\d | Определяет символ цифры. Эквивалентно [0-9] | цифры от 0-9 | |
\D | Любой символ который не является цифрой. Эквивалентно [^0-9] | Не цифра (любой символ кроме символов 0-9) | |
\n | Перевод строки. | ||
\t | Горизонтальная табуляция | ||
\v | Вертикальная табуляция | ||
\f | Конец страницы | ||
\b | Литерал «забой» внутри символьного класса. Вне символьного класса это метасимвол границы слова. | Символ на границе слова (в начале или в конце) | |
\0 | Литерал«NULL» (пустой символ). |
Экранирование
Чтобы использовать метасимвол, как литерал, его нужно экранировать обратным слэшем.
1 |
Регулярное выражение \(\.\) задаёт последовательность литералов (.). |
Позиционные метасимволы | |||
---|---|---|---|
^ | Этот метасимвол означает позицию начала текста. Если регулярное выражение используется с флагом MULTILINE, то этот метасимвол указывает на позицию начала строки многострочного текста. | ^Hello | «Hello, world», но не «Ok, Hello world» т.к. в этой строке слово «Hello» находится не в начале |
$ | Этот метасимвол означает позицию конца текста. Если регулярное выражение используется с флагом MULTILINE, то этот метасимвол указывает на позицию конца строки многострочного текста. | Hello$ | «World, Hello» |
\b | Граница слова. Словом считаются буквенные литералы, цифры и нижнее подчёркивание, идущие подряд. Границы слова, соответственно, находятся слева и справа от слова. | \b(my)\b | В строке «Hello my world» выберет слово «my» |
\B | Соответствует позиции, не являющейся границей слов. | \B(ld)\b | Соответствие найдется в слове «World», но не в слове «ld» |
\G | Позиция конца предыдущего совпадения. Если текущее совпадение — первое, то это позиция начала текста. |
Кванторы
Квантификатор | Описание | Шаблон | Число соответствий |
---|---|---|---|
* | Любое количество совпадений или их отсутствие. То же самое, что и ({0,}) | \d*\.\d | «.0», «19.9», «219.9» |
+ | Как минимум одно повторение. То же самое, что и ({1,}) | «be+» | «bee» в «been», «be» в «bent» |
? | Количество совпадений 0 или 1. То же самое, что и {0,1}. | «rai?n» | «ran», «rain» |
{n} | Предыдущий элемент повторяется ровно n раз. | «,\d{3}» | «,043» в «1,043.6», «,876», «,543» и «,210» в «9,876,543,210» |
{n,} | Предыдущий элемент повторяется как минимум n раз. | «\d{2,}» | «166», «29», «1930» |
{n,м} | Совпадения от n до m включительно. | «\d{3,5}» | «166», «17668»
«19302» в «193024» |
*? | Предыдущий элемент не повторяется вообще или повторяется, но как можно меньшее число раз. | \d*?\.\d | «.0», «19.9», «219.9» |
+? | Предыдущий элемент повторяется один или несколько раз, но как можно меньшее число раз. | «be+?» | «be» в «been», «be» в «bent» |
?? | Предыдущий элемент не повторяется или повторяется один раз, но как можно меньшее число раз. | «rai??n» | «ran», «rain» |
{n}? | Предыдущий элемент повторяется ровно n раз. | «,\d{3}?» | «,043» в «1,043.6», «,876», «,543» и «,210» в «9,876,543,210» |
{n,}? | Предыдущий элемент повторяется как минимум n раз (как можно меньше). | «\d{2,}?» | «166», «29», «1930» |
{n,м}? | Предыдущий элемент повторяется не менее n и не более m раз (как можно меньше). | «\d{3,5}?» | «166», «17668»
«193», «024» в «193024» |
Перечисление флагов RegexOptions позволяет настраивать поведение сопоставления.
Структура перечисления RegexOptions
Член | Описание |
---|---|
CultureInvariant | Предписывает игнорировать национальные установки строки |
ExplicitCapture | Модифицирует способ поиска соответствия, обеспечивая только буквальное соответствие |
IgnoreCase | Игнорирует регистр символов во входной строке |
IgnorePatternWhitespace | Удаляет из строки не защищенные управляющими символами пробелы и разрешает комментарии, начинающиеся со знака фунта или хеша |
Multiline | Изменяет значение символов ^ и $ так, что они применяются к началу и концу каждой строки, а не только к началу и концу всего входного текста |
RightToLeft | Предписывает читать входную строку справа налево вместо направления по умолчанию — слева направо (что удобно для некоторых азиатских и других языков, которые читаются в таком направлении) |
Singleline | Специфицирует однострочный режим, в котором точка (.) символизирует соответствие любому символу |
Группы и просмотр
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
//nookery.ru // Простая группа с захватом. ( ) /* Группа без захвата. Выражение: про(?:фессиональное|движение); Результат: Найдены оба слова. */ (?: ) /* Группа с положительной опережающей проверкой. Выражение: говор(?=ит); Результат: Найдено только слово "говорит" (еще нашло бы "говор"). */ (?= ) /* Группа с положительной опережающей проверкой. Выражение: говор(?!ит); Результат: Найдено "говорю", "говори", но не "говорит". */ (?! ) /* Группа с положительной ретроспективной проверкой. Выражение: (?<=об)говорить; Результат: Найдено «говорить» и «обговорить», но не «уговорить». */ (?<= ) /* Группа с отрицательной ретроспективной проверкой. Выражение: (?<!об)говорить; Результат: Найдено «говорить» и «уговорить», но не «обговорить». */ (?<! ) |
Все регулярные выражения для общего понимания можно представить так :
1 |
^([разрешенные и запрещенные символы]{количество повторений})$ |
Распространенным применением RegexOptions является выполнение поиска, нечувствительного к регистру символов:
1 |
Console. WriteLine (Regex .Match ("а", "А", RegexOptions. IgnoreCase)); / / а |
ще одним полезным флагом является IgnorePatternWhi tespace или ( ?х). Он позволяет вставлять пробельные символы, чтобы улучшить читабельность регулярного выражения — без трактовки этих символов литеральным образом.
Методы класса Regex позволяют выполнять следующие операции:
Определить, встречается ли шаблон регулярного выражения во входном тексте, с помощью вызова метода Regex.IsMatch.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//Поиск соответсвия набора символов в шаблоне с начала и до конца строки string pattern = "^[nookery]+$"; string text = "nook"; Regex regex = new Regex(pattern); Console.WriteLine(regex.IsMatch(text)); //True string text2 = "notokery"; Console.WriteLine(regex.IsMatch(text2)); //False //поиск всех символов и цифр string pattern2 = "^[a-z0-9]+$"; string text3 = "nookery2018"; Regex regex2 = new Regex(pattern2); Console.WriteLine(regex2.IsMatch(text3)); //True //Создание шаблона с использованием оператора ИЛИ string pattern3 = "2018|it|fun"; string text4 = "nookery 2018 it's fun!"; Regex regex3 = new Regex(pattern3); Console.WriteLine(regex3.IsMatch(text4)); //True |
Получить один или все экземпляры текста, соответствующего шаблону регулярного выражения с помощью метода Regex.Match или Regex.Matches. Первый метод возвращает объект System.Text.RegularExpressions.Match, который предоставляет сведения о соответствующем тексте. Второй метод возвращает объект MatchCollection, содержащий один объект System.Text.RegularExpressions.Match для каждого соответствия, обнаруженного в обработанном тексте.
Заменить текст, соответствующий шаблону регулярного выражения, с помощью метода Regex.Replace:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//Создаем переменные формата шаблона и меняем их местами. Console.WriteLine(Regex.Replace("07/09/1976", @"(?<месяц>\d{1,2})/(?<день>\d{1,2})\/(?<год>\d{2,4})", "${день}-${месяц}-${год}")); // 09 - 07 - 1976 //замена всех цифр на пробел Console.WriteLine(Regex.Replace("nookery2018ru01it's24fun!", @"\d+", " ")); //nookery ru it's fun! //Прибавляем к каждой цифре один Console.WriteLine(Regex.Replace("5 add to 15 each digit 1", @"\d", x=>(int.Parse(x.Value)+1).ToString())); //6 add to 26 each digit 2 |
Именованные группы
В длинном или сложном выражении работать с группами удобнее по именам, а не по индексам. Ниже приведен переписанный предыдущий пример, в котором применяется группа по имени ‘letter’:
1 2 3 4 5 6 7 |
string regFind = @"<(?'taq'\w+?) .*>" + // соответствует первому дескриптору; назовем группу 'tag' @" (? 'text•. *?)" + //соответствует текстовому содерж:иыому; назовем группу 'text' @"</\k'tag'>"; //соответствует последнему дескриптору, отмеченному как 'tag' Match m = Regex .Match ( "<hl>hello</hl>", regFind); Console.WriteLine (m.Groups ["tag"J); // hl Console.WriteLine (m.Groups ["text"]); // hello |
Замена и разделение текста
Метод RegEx. Replace работает подобно string. Replace за исключением того, что использует регулярное выражение.
1 2 3 4 |
string find = @"\bcat\b"; string replace = "dog"; Console.WriteLine (Regex.Replace ("catapult the cat", find, replace)); ВЫВОД: catapult the dog |
Меняем кошку на собаку.
Разделение текста
Статический метод Regex. Spli t — это более мощная версия метода string. Split с реrулярным выражением, обозначающим шаблон разделителя. В следующем примере мы разделяем строку, в которой любая цифра считается разделителем:
1 2 |
foreach (string s in Regex.Split ("а5Ь7с", @"\d")) Console.Write (s +" "); // а Ь с |
Следующий код разбивает строку в верблюжьем стиле на отдельные слова:
1 2 |
foreach (string s in Regex.Split ("oneTwoThree", @"(?=[A-Z])")) Console. Wri te ( s + " ") ; / / one Two Three |
Группы
Шаблон регулярное выражение, которое представляет телефонные номера в CIIIA, такие как 206-465-1918:
Предположим, что мы хотим разделить его на две группы: код зоны и локальный номер. Этого можно достигнуть, используя круглые скобки для захвата каждой группы:
1 2 3 |
Match m = Regex.Match ("206-465-1918", @"(\d{3})-(\d{3}-\d{4})"); Console.WriteLine (m.Groups[l]); // 206 Console.WriteLine (m.Groups[2]); // 465-1918 |
Ниже буду приводить разные шаблоны с которыми сталкиваюсь, может быть и вам пригодиться:
Шаблон на ввод логина, позволяет осуществить поиск и исключить все знаки, оставив только буквы анг и рус и цифры.
1 2 3 4 5 6 7 8 9 |
public static bool Check(string str) { string pattern= @"^[А-Яа-яA-Za-z0-9]+$"; Regex regular = new Regex(pattern); if (regular.IsMatch(str)) return true; else return false; } |
Шаблон поиск внутри большой строки:
1 2 3 4 5 6 |
Match m = Regex.Match ("any colour you like", @"colou?r"); Console.WriteLine (m.Success); !! True Console.WriteLine (m. Index); 11 4 Console.WriteLine (m. Length); 11 6 Console.WriteLine (m.Value); !! colour Console.WriteLine (m. ToString ()); 11 colour |
Шаблон проверки сложности паролей:
Следующий код проверяет, что пароль состоит, по меньшей мере, из шести символов и включает цифру, символ или знак пунктуации:
1 2 3 4 |
string r = @"(?х)"(?=.* ( \d 1 \р{Р} 1 \p{S} )) . {б,} "; Console.WriteLine (Regex.IsMatch ("аЬс12", r)); // False Console.WriteLine (Regex.IsMatch ("abcdef", r)); // False Console.WriteLine (Regex.IsMatch ("ab88yz", r)); // True |
Шаблон Разбор даты/времени:
Это выражение поддерживает разнообразные числовые форматы даты и работаетнезависимо от того, где указан год — в начале или конце. Директива ( ?х) улучшает читабельность, разрешая применение пробельных символов; директива ( ? i) отключает чувствительность к регистру символов (для необязательного указателя АМ/РМ). Затем к компонентам совпадения можно обращаться через коллекцию Groups:
1 2 3 4 5 6 7 8 9 |
string r = @" (?х) (?i) (\d{l,4}) [./-] (\d{l,2)) [./-] (\d{l,4}) [\sT] (\d+): (\d+): (\d+) \s? (А\. ?М\.? 1 Р\. ?М\. ?) ?"; string text = "01/02/2008 5:20:50 РМ"; foreach (Group g in Regex.Match (text, r) .Groups) Console.WriteLine (g.Value +" "); 01/02/2018 5:20:50 РМ 01 02 2018 5 20 50 РМ |
Шаблон проверки соответствие римским числам:
1 2 3 4 5 6 7 8 |
string r = @" (?i) \Ьш*" + @"(d?c{O,З}lc[dm])" + @"(l?x{O,З)lx[lc])" + @"(v?i{O,З)li[vx])" + @ "\Ь"; Console.WriteLine (Regex.IsMatch ("MCMLXXXIV", r)); /1 True |
Шаблон удаление повторяющихся слов:
Здесь мы захватываем именованную группу dupe:
1 2 3 4 |
string r = @" (? 'dupe' \w+) \W\k 'dupe• "; string text = "In the the beginning ... "; Console.WriteLine (Regex.Replace (text, r, "${dupe}")); In the beginning |
Шаблон подсчет слов:
1 2 3 |
string r = @"\b(\wl [-'] ) +\Ь"; string text = "It' s all rnumЬo-jumЬo to rne"; Console.WriteLine (Regex.Matches (text, r) .Count); // 5 |
Шаблон преобразование символов в строке запроса НTTР:
1 2 3 4 5 6 7 8 |
string sample = "С%23 rocks"; string result = Regex.Replace sample, @"% [0-9a-f] [0-9a-f] ", m => ((char) Convert.ToByte (m.Value.Substring (1), 16)) .ToString(), RegexOptions.IgnoreCase ) ; Console.WriteLine (result); // С# rocks |
Шаблон разбор поисковых терминов Google из журнаnа веб-статистики:
Это должно использоваться в сочетании с предыдущим примером преобразования символов в строке запроса:
1 2 3 4 5 6 7 |
string sample = "http://google.com/search?hl=en&q=greedy+quantifiers+regex&btnG=Search"; Match m = Regex.Match (sample, @"(?<=google\ .. +search\?.*q=) .+?(?=(&!$))"); string[] keywords = m.Value.Split ( new[] { '+' }, StringSplitOptions.RemoveEmptyEntries}; foreach (string keyword in keywords) Console. Wri te (keyword + " "}; 11 greedy quantifiers regex |
Шаблон поиска ссылок в HTML странице:
1 2 3 4 5 6 7 |
string pattern = @"href='(?<ссылка>\S+)'>"; Regex regul = new Regex(pattern); var matches = regul.Matches(link); foreach (Match m in matches) { richTextBox1.Text += m.Groups["ссылка"]+Environment.NewLine; } |
Шаблон нахождения номера телефона начинающегося с +7
1 2 3 4 5 6 7 8 |
string patternPhone = @"\+7\d{10}"; Regex r = new Regex(patternPhone); var tel = r.Matches(link); foreach (Match m in tel) { richTextBox2.Text += m.Value + Environment.NewLine; } |
Еще один пример:
1 2 3 4 5 |
regex = new Regex(@"(?<phone>[+3(0-90-90-9)\s]{2,}[0-9]{3}[\s\-][0-9]{2}[\s\-][0-9]{2})"); foreach (Match m in regex.Matches(responseFromServer)) { writer.WriteLine("Тел. номер: {0,-25}", m.Groups["phone"]); } |
Шаблон проверки правильности ввода email
1 |
"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$" |
Шаблон позволяющий найти EMAIL в HTML странице:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
string patternEmail = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"; //еще один вариант string patternEmail = @"\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}\b"; //еще вариант regex = new Regex(@"(?<email>[0-9A-Za-z_.-]+@[0-9a-zA-Z-]+\.[a-zA-Z]{2,4})"); foreach (Match m in regex.Matches(responseFromServer)) { writer.WriteLine("E-Mail: {0,-25}", m.Groups["email"]); } |
Шаблон позволяющий заменить в тексте все предлоги состоящие из 2 символов, названием сайта:
1 2 3 |
string pattern = @"\s[а-я]{1,2}\s"; var r= Regex.Replace(str,pattern," nookery "); Console.WriteLine(r); |
Шаблон позволяющий заменить в тексте, табуляцию \t , перевод строки \n, возврат каретки \r , а так же все цифры и набор символов, на пробел
1 2 3 4 |
string pattern = @"[\r\n\t,”\“—…‘*// \d+]"; string target = " "; Regex regex = new Regex(pattern); text = regex.Replace(text, target); |
И в конце приведу шпаргалку, которую вы сможете скаать распечатать или сохранить у себя. Позволяет быстро освежить память по регуляркам.
Скачать Regular.png