Программирование и IT
Регулярные выражения
Регулярное выражение — это компактное описание множества строк. Один и тот же синтаксис работает в grep, в редакторе кода, в JavaScript, PHP, Java и Python, поэтому выучить его достаточно один раз. Здесь разобран сам язык шаблонов, а не библиотека конкретного языка.
В этой статье
Что именно делает шаблон#
Механизм сопоставления берёт строку и идёт по ней слева направо, пытаясь
приложить шаблон к очередной позиции. Совпало — движок сообщает найденный
кусок и его границы; не совпало — сдвигается на символ вправо и пробует снова.
Из этого следует важное: шаблон по умолчанию ищет подстроку где угодно, а не
проверяет строку целиком. «Проверить весь ввод» — отдельная задача, она
решается якорями.
Символы шаблона делятся на два сорта. Обычные означают сами себя: кот
находит буквы «к», «о», «т» подряд. Метасимволы — . ^ $ * + ? ( ) [ ] { } | \
— имеют особый смысл, и чтобы найти их буквально, перед ними ставят обратный
слэш: \. — точка, \$ — знак доллара, \\ — сам слэш.
Наборы символов#
Квадратные скобки описывают один символ из перечисленного. Внутри скобок почти
все метасимволы теряют силу: [.+] — это точка или плюс, экранировать их не
нужно.
| Запись | Совпадает с |
|---|---|
[abc] |
одной буквой a, b или c |
[a-z] |
любой строчной латинской буквой |
[А-Яа-яЁё] |
русской буквой в любом регистре |
[0-9] |
цифрой |
[^0-9] |
чем угодно, кроме цифры |
[-+*/] |
одним из четырёх знаков (дефис первым — он буквальный) |
Крышка ^ меняет смысл набора на противоположный только сразу после открытой
скобки; в середине — [a^b] — это обычный символ.
Частые наборы имеют короткие имена. Они есть почти везде, кроме классического
POSIX-синтаксиса:
| Сокращение | Равносильно | Отрицание |
|---|---|---|
\d |
цифра | \D |
\w |
буква, цифра или подчёркивание | \W |
\s |
пробел, табуляция, перевод строки | \S |
. |
любой символ, кроме перевода строки | — |
Сколько раз повторить#
Квантификатор относится к тому, что стоит слева: к одному символу, к набору в
скобках или к целой группе.
| Запись | Смысл |
|---|---|
? |
ноль или один раз |
* |
ноль и больше раз |
+ |
один и больше раз |
{3} |
ровно три раза |
{2,5} |
от двух до пяти |
{2,} |
два и больше |
Разница между * и + ловит новичков: -* совпадёт и с пустотой, то есть
такой шаблон «находится» в любой строке. Если элемент обязателен, ставь +.
Квантификатор после группы повторяет всю группу целиком: (ab)+ находит
«abababab», а ab+ — «abbbb».
Якоря и границы#
| Запись | Привязка |
|---|---|
^ |
начало строки |
$ |
конец строки |
\b |
граница слова |
\B |
место, которое границей не является |
Пара ^…$ превращает поиск подстроки в проверку всего значения — так пишут
валидацию форм. Шаблон ^\d{6}$ подтвердит почтовый индекс из шести цифр и
отвергнет строку «12345678», тогда как \d{6} без якорей в ней найдётся.
Граница \b стоит между символом слова и не-словом. Поэтому \bкот\b найдёт
отдельное слово и пропустит «котлета» и «скот». Это самый быстрый способ
починить поиск, который «находит лишнее».
Режим многострочности переключает смысл ^ и $ — с краёв всего текста на
края каждой строки. Он включается флагом (обычно m), и без него шаблон
^Ошибка в многострочном журнале найдёт в лучшем случае одно совпадение.
Группы и альтернатива#
Круглые скобки делают две вещи сразу: объединяют кусок шаблона в единое целое и
запоминают то, что в него попало.
(...)— захватывающая группа, её содержимое доступно по номеру: 1, 2, 3 —
слева направо по открывающим скобкам;(?:...)— группировка без запоминания, когда скобки нужны только ради
структуры;(?<name>...)— именованная группа, обращение по имени вместо номера;|— альтернатива, «или»:кот|пёс|хорёк.
Альтернатива имеет самый низкий приоритет, поэтому её почти всегда берут в
скобки. Шаблон ^да|нет$ читается как «строка начинается с „да“ ИЛИ
заканчивается на „нет“», а нужное «строка целиком равна да или нет» пишется
как ^(да|нет)$.
Внутри самого шаблона к запомненному куску обращаются через \1, \2 —
обратная ссылка. Шаблон \b(\w+) \1\b находит случайно удвоенное слово, а
(["']).*?\1 — строку в кавычках, закрытую тем же знаком, каким открыта.
Жадность#
Квантификаторы по умолчанию хватают максимум и отступают только под давлением.
Классический промах — разбор пары скобок:
строка: (раз) и (два)
шаблон: \(.*\) → находит (раз) и (два)
шаблон: \(.*?\) → находит (раз) и отдельно (два)
шаблон: \([^)]*\) → то же самое, но быстрее
Знак ? сразу после квантификатора делает его ленивым: *?, +?, {2,5}?.
Третий вариант — через отрицающий набор — не требует возвратов вообще и на
длинных строках работает заметно быстрее; когда «до первого такого-то символа»
выражается набором, выбирай его.
Готовые шаблоны#
| Задача | Шаблон |
|---|---|
| Целое число, возможно со знаком | ^-?\d+$ |
| Число с дробной частью | ^-?\d+([.,]\d+)?$ |
| Дата в формате ГГГГ-ММ-ДД | ^\d{4}-\d{2}-\d{2}$ |
Цвет HTML вида #a1b2c3 |
^#[0-9a-fA-F]{6}$ |
| Российский телефон из 11 цифр | ^\+?[78]\d{10}$ |
| Адрес страницы | https?://\S+ |
| Два и больше пробела подряд | \s{2,} |
| Пустая строка или одни пробелы | ^\s*$ |
| Слово целиком | \b\w+\b |
Отдельно — время суток, потому что диапазон 00:00–23:59 одним набором не
описывается:
^([01]\d|2[0-3]):[0-5]\d$
Здесь [01]\d покрывает часы с 00 до 19, альтернатива 2[0-3] добавляет
20–23, а минуты [0-5]\d не пропустят «:60». Так же строятся любые числовые
диапазоны — по разрядам, а не одним выражением.
Проверки почты и адреса в таблице намеренно грубые. Полная регулярка для
электронной почты по стандарту занимает несколько сотен знаков и всё равно не
доказывает, что ящик существует; на практике достаточно отсечь очевидный мусор
шаблоном ^\S+@\S+\.\S+$, а реальную проверку сделать письмом с кодом.
Где шаблоны отличаются#
Единого стандарта нет, есть семейства диалектов.
grepбез ключей понимает базовый POSIX, где+,?,|и скобки
групп нужно экранировать. Ключ-Eвключает расширенный синтаксис, и
шаблон становится привычным:grep -E "^[0-9]{4}-" файл.- Сокращения
\dи\w— расширение Perl. В POSIX вместо них пишут
[[:digit:]]и[[:alnum:]_]. В GNU grep они доступны с ключом-P. - Разные языки по-разному относятся к Unicode: где-то
\w— только латиница,
где-то включает кириллицу. Когда сомневаешься, пиши набор явно. - Именованные группы записываются как
(?<name>…)в большинстве реализаций и
как(?P<name>…)в Python.
Как эти шаблоны вызываются из кода на Python — в статье про
модуль re: там разобраны функции
поиска, замены и флаги. Поиск по файлам из терминала — в разборе
команд Linux, раздел про grep.
Как отлаживать и когда не применять#
Шаблон собирают по кускам. Сначала проверь, что находится самая простая
часть, потом добавляй по одному элементу, каждый раз прогоняя на тестовой
строке. Тестовых строк нужно две: та, что обязана совпасть, и похожая, которая
совпасть не должна — иначе легко написать шаблон, находящий всё подряд.
Длинные шаблоны разбивают на строки с комментариями: режим x (verbose)
разрешает пробелы и комментарии внутри выражения и есть в большинстве языков.
Читаемость важнее краткости — через месяц автор и сам не узнает свою однострочную
конструкцию.
Есть задачи, где регулярка — неверный инструмент. HTML и XML разбирают
парсером: вложенность произвольной глубины регулярным выражением не
описывается в принципе. JSON читают библиотекой. Строку с фиксированным
разделителем проще разрезать функцией разбиения, чем шаблоном.
Отдельно стоит помнить о цене: вложенные квантификаторы вроде (a+)+$ на
неподходящей строке заставляют движок перебирать экспоненциальное число
вариантов, и проверка формы вешает сервер. Признак опасности — квантификатор
внутри группы, к которой применён ещё один квантификатор.
План по этапам
- Наборы и кванторыСобрать шаблон для целого числа, затем для числа со знаком и дробной частью, проверив на пяти строках.
- ЯкоряПревратить поиск подстроки в проверку всего значения через ^ и $, убедиться на строке с лишними символами по краям.
- ГруппыРазобрать дату на три группы и собрать из них обратный порядок через ссылки \1 и \3.
- ЖадностьПрогнать жадный, ленивый и вариант с отрицающим набором на строке с двумя парами скобок.
- Свой шаблон под задачуНаписать проверку времени суток и протестировать её на 23:59, 24:00 и 12:60.
Начать изучать эту тему у себя
План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.
Проверь себя
1.Сколько непересекающихся совпадений даст шаблон \d{2} в строке 12345?
2.Какой квантификатор означает «ноль или один раз»?
3.Что означает запись [^0-9]?
4.Сколько совпадений даст жадный шаблон a+ в строке aaabaa?
Источники
-
Регулярные выражения на MDNПолный разбор синтаксиса с примерами и таблицами спецсимволовбесплатно
-
Руководство GNU grepРазличия базового, расширенного и perl-синтаксиса из первых рукбесплатно
-
Regular Expression HOWTOПошаговое введение, полезное и вне Pythonбесплатно
Было полезно?