Программирование и 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. Наборы и кванторыСобрать шаблон для целого числа, затем для числа со знаком и дробной частью, проверив на пяти строках.
  2. ЯкоряПревратить поиск подстроки в проверку всего значения через ^ и $, убедиться на строке с лишними символами по краям.
  3. ГруппыРазобрать дату на три группы и собрать из них обратный порядок через ссылки \1 и \3.
  4. ЖадностьПрогнать жадный, ленивый и вариант с отрицающим набором на строке с двумя парами скобок.
  5. Свой шаблон под задачуНаписать проверку времени суток и протестировать её на 23:59, 24:00 и 12:60.

Начать изучать эту тему у себя

План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.

Начать план

Проверь себя

1.Сколько непересекающихся совпадений даст шаблон \d{2} в строке 12345?

2.Какой квантификатор означает «ноль или один раз»?

3.Что означает запись [^0-9]?

4.Сколько совпадений даст жадный шаблон a+ в строке aaabaa?

Источники

Было полезно?

Ещё темы

Программирование и IT Python: регулярные выражения Регулярное выражение — шаблон, описывающий множество строк. В Python с ними работает модуль `re` из стандартной библиотеки. Шаблон пишут в строке с префиксом `r`, ищут через `re.search`, а результат достают из объекта совпадения. Всё остальное — детали языка шаблонов. Программирование и IT Python: генераторы Генератор выдаёт значения по одному и только тогда, когда их просят. Он не хранит всю последовательность в памяти, поэтому подходит для больших файлов и бесконечных потоков. Плата за это — генератор одноразовый: пройти по нему дважды не получится. Программирование и IT SQL-запросы: примеры и разбор Запрос на SQL описывает, какие строки нужны, а не как их искать. Ниже одни и те же две таблицы проходят через все основные конструкции языка — от простого отбора до соединений и подзапросов, и на каждый запрос показан результат до последней строки. Программирование и IT Как изучить Python с нуля Python хорош для первого знакомства с программированием: код читается почти как текст, а стандартная библиотека закрывает большинство бытовых задач. Этот план ведёт от установки интерпретатора до собственных скриптов, покрытых тестами, — примерно за четыре месяца при занятиях около часа в день. Программирование и IT Как изучить SQL с нуля SQL — язык запросов к реляционным базам данных. Он нужен разработчикам, аналитикам, тестировщикам и менеджерам, которые хотят сами доставать цифры. Базовые запросы осваиваются за несколько недель, уверенная работа со сложными отчётами — за два-три месяца практики. Ниже — порядок тем и способы тренироваться на настоящей базе. Программирование и IT Как изучить Linux с нуля Linux работает на большинстве серверов, в контейнерах и на множестве устройств, поэтому командная строка нужна разработчику, тестировщику, аналитику и системному администратору. Осваивать систему удобнее всего не чтением списков команд, а ежедневной работой в терминале и решением небольших практических задач. Ниже — последовательность тем на два-три месяца.

Ещё сценарии