Справочник · Код и сборка
Регулярные выражения: синтаксис и готовые шаблоны
Классы символов, квантификаторы, группы, просмотр вперёд и назад, флаги и различия между JavaScript, Python и grep.
Регулярное выражение описывает шаблон строки. Движок идёт по тексту и пытается сопоставить шаблон с каждой позиции по очереди. Большая часть синтаксиса одинакова в JavaScript, Python, PCRE, Go и Java, различия в деталях собраны в конце. Проверять шаблоны удобно в тестере регулярных выражений.
Символы и классы
| Запись | Что совпадает |
|---|---|
. | любой символ, кроме перевода строки (с флагом s — и его тоже) |
\d | цифра; в JS — только 0-9, в Python 3 — любая цифра Юникода |
\w | буква, цифра или _; в JS только латиница, в Python — любые буквы |
\s | пробельный символ: пробел, табуляция, переводы строк |
\D, \W, \S | всё, кроме соответствующего класса |
[abc] | один из перечисленных символов |
[^abc] | любой символ, кроме перечисленных |
[a-z0-9] | диапазоны |
[.] | точка как обычный символ: внутри скобок она не особенная |
\. \* \( | экранирование особых символов |
\t \n \r | табуляция, перевод строки, возврат каретки |
А | символ по коду (JS, Python) |
Квантификаторы
| Запись | Сколько раз |
|---|---|
* | 0 и больше |
+ | 1 и больше |
? | 0 или 1 |
{3} | ровно 3 |
{2,5} | от 2 до 5 |
{2,} | 2 и больше |
*? +? ?? {2,5}? | то же, но «ленивые»: как можно меньше |
По умолчанию квантификаторы жадные: захватывают максимум и отступают, только если дальше шаблон не сходится. Разница на примере строки <b>один</b> и <b>два</b>:
<b>.*</b> совпадает со всей строкой целиком
<b>.*?</b> совпадает с <b>один</b>, затем с <b>два</b>
<b>[^<]*</b> то же, что ленивый вариант, но быстрееЯкоря и границы
| Запись | Где |
|---|---|
^ | начало строки (с флагом m — начало каждой строки) |
$ | конец строки (с флагом m — конец каждой строки) |
\b | граница слова: между \w и не \w |
\B | не граница слова |
\A, \Z | начало и конец всего текста (Python, PCRE; в JS нет) |
Якоря не потребляют символы, они проверяют позицию. Без ^ и $ шаблон \d{4} найдёт четыре цифры и внутри 123456.
Группы и обратные ссылки
| Запись | Смысл |
|---|---|
(abc) | группа с захватом, доступна как $1 или \1 |
(?:abc) | группа без захвата, только для объединения |
(?<year>\d{4}) | именованная группа (в Python также (?P<year>...)) |
\1 | то, что захватила первая группа, внутри шаблона |
\k<year> | ссылка на именованную группу |
a|b | или a, или b |
Пример: найти повторяющееся слово «на на».
\b(\w+)\s+\1\bЗамена с группами переставляет части даты из 2026-11-03 в 03.11.2026:
"2026-11-03".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3.$2.$1");
"2026-11-03".replace(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/, "$<d>.$<m>.$<y>");В Python в строке замены пишут \3.\2.\1 или \g<d>.
Просмотр вперёд и назад
Проверяют, что рядом есть (или нет) какой-то текст, но не включают его в совпадение.
| Запись | Условие |
|---|---|
X(?=Y) | за X идёт Y |
X(?!Y) | за X не идёт Y |
(?<=Y)X | перед X стоит Y |
(?<!Y)X | перед X не стоит Y |
\d+(?=\s?₽) число перед знаком рубля
(?<=\$)\d+(\.\d\d)? сумма после знака доллара
^(?=.*\d)(?=.*[a-z]).{8,}$ не меньше 8 символов, есть цифра и строчная буква
\b(?!test)\w+\.py\b имена .py-файлов, не начинающиеся с testФлаги
| Флаг | JS | Python | Что меняет |
|---|---|---|---|
| без учёта регистра | i | re.I | a совпадает с A |
| многострочный | m | re.M | ^ и $ работают на каждой строке |
| точка и перевод строки | s | re.S | . совпадает и с \n |
| глобальный | g | — | найти все совпадения (в Python — findall, finditer) |
| Юникод | u | по умолчанию | корректные суррогатные пары, \p{...} |
| подробный | — | re.X | пробелы и комментарии внутри шаблона |
| липкий | y | — | искать только с позиции lastIndex |
Готовые шаблоны
Это рабочие, но упрощённые шаблоны. Для строгой проверки формата лучше использовать парсер.
| Что | Шаблон |
|---|---|
| целое число со знаком | ^[+-]?\d+$ |
| десятичное число | ^[+-]?(\d+([.,]\d*)?|[.,]\d+)$ |
| дата ГГГГ-ММ-ДД | ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$ |
| время ЧЧ:ММ | ^([01]\d|2[0-3]):[0-5]\d$ |
| IPv4 | ^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$ |
| шестнадцатеричный цвет | ^#([0-9a-fA-F]{3}){1,2}$ |
| почта (грубо) | ^[^\s@]+@[^\s@]+\.[^\s@]+$ |
| UUID | ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ |
| слаг для адреса | ^[a-z0-9]+(-[a-z0-9]+)*$ |
| семантическая версия (кратко) | ^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ |
| пробелы в конце строк | [ \t]+$ с флагом m |
| пустые строки | ^\s*$ с флагом m |
Различия между инструментами
В командной строке встречаются три диалекта:
| Где | Диалект | Особенности |
|---|---|---|
grep, sed | BRE, базовый | + ? | ( ) { } надо экранировать: \( \+ |
grep -E, sed -E, awk | ERE, расширенный | скобки и + работают как обычно; нет \d, вместо него [0-9] или [[:digit:]] |
grep -P, Perl, PCRE | Perl-совместимый | есть \d, ленивые квантификаторы, просмотр вперёд и назад |
grep -E '^[0-9]{3}-[0-9]{4}$' phones.txt
grep -oP '(?<=id=)\d+' access.log
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/g' dates.txtКлассы POSIX: [[:alpha:]], [[:digit:]], [[:space:]], [[:upper:]]. Они учитывают текущую локаль, поэтому в UTF-8 [[:alpha:]] совпадает и с кириллицей.
Производительность и ловушки
- Вложенные квантификаторы вроде
(a+)+$или(\w+\s?)*$на неудачной строке дают экспоненциальный перебор вариантов. Это называется катастрофическим откатом и может повесить сервер. Пишите классы так, чтобы части шаблона не могли совпасть с одним и тем же текстом. - Отрицательный класс
[^"]*почти всегда лучше, чем.*?: движку не нужно откатываться. - Не забывайте экранировать точку в доменах и версиях:
1.2.3как шаблон совпадёт и с1x2y3. - В JS объект
RegExpс флагомgпомнитlastIndex. Повторный вызовtest()на той же строке может вернутьfalse. - В строковых литералах обратную косую черту приходится удваивать:
new RegExp("\\d+"). В Python пишите сырые строкиr"\d+".