Регулярные выражения: полное руководство
Синтаксис regex, метасимволы, квантификаторы, группы, примеры, тестирование, частые паттерны.
Введение
Регулярные выражения (regex, regexp) — это компактный язык для описания шаблонов в тексте. С их помощью можно найти все email-адреса в документе, проверить, что телефон соответствует формату, извлечь домен из URL, заменить все вхождения слова с учётом словоформ, разобрать лог-файл на поля. Regex используется везде: от grep в командной строке до валидации форм в JavaScript, от парсинга логов в Python до написания компиляторов. В этом руководстве разберём синтаксис, метасимволы, квантификаторы, группы, lookahead и lookbehind, частые паттерны и типичные ошибки.
Базовый синтаксис
Регулярное выражение — это строка, в которой обычные символы означают сами себя, а специальные (метасимволы) задают правила. Простейший regexp — это просто строка:/hello/ найдёт подстроку «hello» в любом месте текста. Сложность начинается с метасимволов.
| Метасимвол | Значение | Пример |
|---|---|---|
. | Любой символ (кроме переноса строки) | a.c → «abc», «a1c», «a c» |
\d | Цифра (0–9) | \d{3} → «123» |
\D | Не цифра | \D+ → «abc» |
\w | Буква, цифра или подчёркивание | \w+ → «hello_42» |
\W | Не \w | — |
\s | Пробельный символ | \s+ → « » |
\S | Не пробельный | — |
^ | Начало строки | ^Hello → «Hello» в начале |
$ | Конец строки | world$ → «world» в конце |
\b | Граница слова | \bcat\b → «cat», но не «category» |
Квантификаторы
Квантификаторы указывают, сколько раз повторяется предыдущий элемент. Это один из самых мощных механизмов regex — позволяет описывать последовательности произвольной длины.
| Квантификатор | Значение | Пример |
|---|---|---|
* | 0 или более раз | ab*c → «ac», «abc», «abbc» |
+ | 1 или более раз | ab+c → «abc», «abbc» (не «ac») |
? | 0 или 1 раз | colou?r → «color», «colour» |
{n} | Ровно n раз | \d{4} → «2025» |
{n,} | n или более раз | \d{3,} → «123», «12345» |
{n,m} | От n до m раз | \d{2,4} → «12», «123», «1234» |
По умолчанию квантификаторы «жадные» (greedy) — захватывают максимально возможную часть текста. Это часто приводит к неожиданным результатам. Например,/".*"/ для строки "hello" "world" захватит всю строку целиком, включая обе кавычки, потому что * жадный. Чтобы сделать квантификатор «ленивым» (lazy), добавьте ?: /".*?"/найдёт две отдельные строки в кавычках.
const text = '"hello" "world"';
// Жадный квантификатор
text.match(/".*"/g);
// → ['"hello" "world"'] (одно совпадение, вся строка)
// Ленивый квантификатор
text.match(/".*?"/g);
// → ['"hello"', '"world"'] (два совпадения)Классы символов и альтернативы
Класс символов — это набор символов в квадратных скобках, любой из которых может стоять в данной позиции. [aeiou] — любая гласная,[0-9] — то же, что \d, [a-zA-Z] — любая латинская буква. Знак ^ в начале класса инвертирует его:[^0-9] — любой символ, кроме цифр.
Альтернативы задаются через |: cat|dog|bird — любое из трёх слов. Альтернативы работают как логическое «или» и могут комбинироваться с группами: (cat|dog)s → «cats» или «dogs».
// Найти все слова, начинающиеся с гласной
const text = "Apple orange Banana apricot Cherry";
text.match(/\b[aeiouAEIOU]\w*/g);
// → ['Apple', 'orange', 'apricot']
// Валидация телефонного номера: +7 (XXX) XXX-XX-XX
const phoneRegex = /^\+7 \(\d{3}\) \d{3}-\d{2}-\d{2}$/;
phoneRegex.test("+7 (495) 123-45-67"); // true
phoneRegex.test("+74951234567"); // falseГруппы и обратные ссылки
Скобки () создают группу — часть шаблона, которую можно использовать как единое целое. Группы нужны для трёх вещей: применения квантификаторов к подвыражению ((ab)+), извлечения частей совпадения и создания альтернатив ((cat|dog)). Каждая группа получает номер (1, 2, 3...) в порядке открывающей скобки, и к ней можно обратиться через обратную ссылку\1, \2 и т. д.
// Извлечение даты: YYYY-MM-DD
const dateRegex = /(\d{4})-(\d{2})-(\d{2})/;
const match = "2025-04-09".match(dateRegex);
// match[1] = "2025" (год)
// match[2] = "04" (месяц)
// match[3] = "09" (день)
// Обратная ссылка: найти повторяющиеся слова
const dupRegex = /\b(\w+)\s+\1\b/gi;
"hello hello world world".match(dupRegex);
// → ['hello hello', 'world world']
// Негруппирующая скобка (?:...)
const httpsRegex = /https?:\/\/(?:www\.)?example\.com/;Если группа нужна только для логики, но не для извлечения, используйте негруппирующую конструкцию (?:...). Это ускоряет работу и упрощает нумерацию — негруппирующие скобки не получают номеров.
Lookahead и Lookbehind
Lookaround — это проверки, которые «заглядывают» вперёд или назад, не включая проверенный текст в совпадение. Lookahead позитивный (?=...) — совпадение, если после текущей позиции идёт указанный шаблон. Lookahead негативный (?!...) — совпадение, если НЕ идёт. Lookbehind позитивный (?<=...) и негативный (?<!...) — аналогично для предшествующего текста.
// Найти число, после которого идёт "px"
"100px 200em 300px".match(/\d+(?=px)/g);
// → ['100', '300']
// Найти число, после которого НЕ идёт "px"
"100px 200em 300px".match(/\d+(?!px)/g);
// → ['200']
// Найти сумму в рублях, перед которой идёт "price: "
"price: 1000₽, tax: 200₽".match(/(?<=price: )\d+/g);
// → ['1000']
// Валидация пароля: минимум 8 символов, есть буква и цифра
const passwordRegex = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/;
passwordRegex.test("abc12345"); // true
passwordRegex.test("12345678"); // false (нет буквы)
passwordRegex.test("abc"); // false (короткий)Флаги
Флаги модифицируют поведение регулярного выражения. В JavaScript они ставятся после закрывающего слеша: /pattern/gi.
| Флаг | Значение |
|---|---|
g | Global — искать все совпадения, а не только первое |
i | Case-insensitive — игнорировать регистр |
m | Multiline — ^ и $ для каждой строки, а не только начала/конца текста |
s | Single-line (dotall) — . совпадает и с переносом строки |
u | Unicode — корректная обработка Unicode-символов (включая кириллицу) |
y | Sticky — поиск строго с позиции lastIndex |
Флаг u особенно важен для работы с кириллицей. Без него\w не включает русские буквы, и шаблон /^\w+$/ не matches «Привет». С флагом u используйте \p{L} для любой буквы любого языка.
// Кириллическое слово (с флагом u)
/^\p{L}+$/u.test("Привет"); // true
/^\w+$/u.test("Привет"); // false (\w без u не понимает кириллицу)
// Поиск с игнорированием регистра
"Hello World".match(/world/i); // → ['World']
// Multiline: ^ и $ для каждой строки
"line1\nline2\nline3".match(/^line\d/gm);
// → ['line1', 'line2', 'line3'] (без m было бы только 'line1')Частые паттерны
// Простая валидация email
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test("user@example.com"); // true
// Более строгая (RFC 5322 приближение)
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/URL
// Протокол + домен + путь
const urlRegex = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/;
urlRegex.test("https://example.com/path?q=1"); // trueIPv4
// IP-адрес версии 4
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
ipv4Regex.test("192.168.1.1"); // true
ipv4Regex.test("256.1.1.1"); // falseДата в формате ISO
// YYYY-MM-DD
const dateRegex = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/;
dateRegex.test("2025-04-09"); // true
dateRegex.test("2025-13-01"); // false (нет 13-го месяца)Цвет в HEX
// #RGB или #RRGGBB
const hexRegex = /^#(?:[0-9a-fA-F]{3}){1,2}$/;
hexRegex.test("#fff"); // true
hexRegex.test("#a1b2c3"); // true
hexRegex.test("#12"); // falseJavaScript API
В JavaScript regex используется через методы строк и через объект RegExp. Основные методы: String.prototype.match — поиск первого или всех совпадений, String.prototype.test — проверка наличия,String.prototype.replace — замена, String.prototype.split— разбиение по шаблону, RegExp.prototype.exec — пошаговый поиск с извлечением групп.
// Проверка
/^\d+$/.test("12345"); // true
// Поиск всех совпадений
"one 1, two 2, three 3".match(/\d/g); // ['1', '2', '3']
// Замена с использованием групп
"2025-04-09".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3.$2.$1");
// → "09.04.2025"
// Именованные группы (ES2018+)
const m = "2025-04-09".match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
m.groups.year; // "2025"
m.groups.month; // "04"
m.groups.day; // "09"
// Разбиение по нескольким разделителям
"a, b; c|d".split(/[,;|]\s*/); // ['a', 'b', 'c', 'd']
// Пошаговый поиск через exec
const regex = /\d+/g;
let match;
while ((match = regex.exec("one 1 two 22 three 333")) !== null) {
console.log(match[0], "at", match.index);
}Типичные ошибки
Жадные квантификаторы
Самая частая ошибка — забыть, что * и + жадные. При извлечении HTML-тегов /<.*>/ на строке<a>text</a> найдёт всю строку целиком, а не отдельные теги. Решение: ленивый квантификатор /<.*?>/ или негативный класс /<[^>]*>/.
Catastrophic backtracking
Некоторые комбинации вложенных квантификаторов приводят к экспоненциальному росту времени работы. Например, /(a+)+b/ на строке из 30 «a» без завершающей «b» будет парситься секунды или минуты. Это используется в ReDoS-атаках (Regular Expression Denial of Service). Решение: избегать вложенных квантификаторов с пересекающимися классами, использовать possessive quantifiers (если поддерживаются) или атомарные группы.
Неэкранированные спецсимволы
Точка . в regex означает «любой символ», но часто нужна как обычная точка — например, в IP-адресе или домене. Не забывайте экранировать:/192\\.168\\.1\\.1/, а не /192.168.1.1/ (последнее matches и «192x168x1x1»). Аналогично с +, *,?, (, ), [, ],{, }, ^, $, |,\\.
Игнорирование Unicode
Без флага u символы вне ASCII (включая кириллицу) обрабатываются некорректно. \w не включает русские буква, . может «разрезать» составной символ. Для работы с кириллицей всегда добавляйте флагu и используйте Unicode-свойства: \p{L} (любая буква),\p{Ll} (строчная), \p{Lu} (заглавная).
Тестирование и отладка
Regex трудно читать и легко ошибиться. Поэтому обязательно тестируйте — на примерах, которые должны совпадать, и на примерах, которые не должны. Удобные инструменты: regex101.com (с подробным разбором), regexr.com, нашregex-тестер. Они показывают совпадения в реальном времени, объясняют каждую часть шаблона, позволяют сохранять и делиться ссылками.
При отладке разбивайте сложный regexp на части. Сначала проверьте, что первая часть находит нужные фрагменты, потом добавьте следующую. Не пытайтесь сразу написать шаблон на 200 символов — он гарантированно будет с ошибками. Документируйте сложные regex-комментариями через (?#...) в языках, которые это поддерживают.
Когда НЕ использовать regex
Regex отлично подходит для поиска и замены по шаблону, но плохо — для парсинга иерархических структур. Не пытайтесь парсить HTML, XML, JSON регулярными выражениями — это путь к страданию и багам. HTML имеет вложенность, атрибуты, комментарии, сущности, и regex не умеет работать с вложенностью. Используйте специализированные парсеры: DOMParser для HTML в браузере, cheerio в Node.js, native JSON.parse для JSON, lxml или ElementTree для XML в Python.
Также regex избыточен для простых проверок. Если нужно проверить, что строка содержит подстроку — используйте includes(). Если нужно проверить начало — startsWith(). Если нужно простое равенство — обычное===. Regex включается тогда, когда шаблон действительно сложный и не выражается через простые строковые операции.
Производительность
Компиляция регулярного выражения занимает время. Если один и тот же regex используется много раз в цикле, вынесите его за пределы цикла и используйте флаг g с методом exec или test сlastIndex. В JavaScript для этого есть RegExp с флагомg и свойство lastIndex. Для сложных проверок можно использовать re2 — библиотеку Google, которая гарантирует линейное время выполнения и не подвержена catastrophic backtracking.
Заключение
Регулярные выражения — мощный инструмент, который окупает время на изучение. Базовый синтаксис (метасимволы, квантификаторы, классы) покрывает 80% задач. Продвинутые возможности (lookahead, lookbehind, именованные группы, флаги) добавляют ещё 15%. Оставшиеся 5% — это действительно сложные случаи, где лучше поискать готовое решение или написать полноценный парсер. Главное правило: не пытайтесь писать regex «вслепую» — всегда тестируйте в специальном инструменте. Используйте наш regex-тестер для экспериментов и проверки шаблонов перед использованием в коде.
Попробуйте эти инструменты
Похожие статьи
Client-side vs Server-side: где обрабатывать файлы
Сравнение обработки файлов в браузере vs на сервере: приватность, скорость, ограничения, безопасность.
Почему браузерные инструменты лучше облачных
Преимущества client-side обработки: приватность, нет загрузки, нет регистрации, скорость, бесплатно.
Конвертация файлов: лучшие практики
Правила конвертации: сохранение качества, выбор формата, batch обработка, автоматизация.
Оптимизация веб-производительности: изображения
Lazy loading, responsive images, современные форматы (WebP, AVIF), CDN, сжатие, влияние на SEO.