Что такое Regex?
Регулярные выражения (regex или regexp) — это последовательности символов, которые определяют паттерны поиска. Они являются одним из самых мощных инструментов для обработки текста, распознавания паттернов и извлечения данных.
Почему использовать регулярные выражения?
Regex необходимы для:
- Валидации форм (email, номера телефонов, пароли)
- Извлечения данных (парсинг логов, скрейпинг веб-страниц)
- Обработки текста (поиск и замена, форматирование)
- Рефакторинга кода (переименование переменных, обновление синтаксиса)
- Санитизации ввода (безопасность, предотвращение injection-атак)
Как читать эту шпаргалку
Это руководство организовано в прогрессивные разделы:
- Основы - Базовый синтаксис и паттерны
- Продвинутые возможности - Lookaround, именованные группы, Unicode
- Особенности языков - Примеры на Python, JavaScript, PHP, C#, Java, Go, Ruby
- Regex в VS Code - Трансформации поиска/замены с более чем 20 примерами
- Распространённые паттерны - Готовые паттерны для email, URL, дат и т.д.
- Практические примеры - Применение в реальном мире
- Устранение проблем - Частые ошибки и советы по производительности
Каждый раздел включает:
- Понятные объяснения
- Визуальные примеры
- Готовые к копированию фрагменты кода
- Профессиональные советы и подводные камни
Литеральные символы
Самый простой regex — это последовательность литеральных символов:
abc
Соответствует: "abc" в "Последовательность abc"
Чувствительность к регистру
По умолчанию regex чувствительны к регистру:
Приветсоответствует "Привет", но НЕ "привет"- Используйте флаг
iдля поиска без учёта регистра:/привет/i
Специальные символы (метасимволы)
Эти 12 символов имеют специальное значение в regex и должны быть экранированы с помощью \ для буквального соответствия:
. ^ $ * + ? { } [ ] \ | ( )
Примеры:
\.соответствует литеральной точке\₽соответствует знаку рубля\(соответствует литеральной скобке
| Символ | Экранирование | Пример | Соответствует |
|---|---|---|---|
. (точка) |
\. |
3\.14 |
"3.14" |
₽ (рубль) |
\₽ |
\₽100 |
"₽100" |
* (звёздочка) |
\* |
a\*b |
"a*b" |
Классы символов
Предопределённые классы символов
| Паттерн | Описание | Эквивалент | Пример | Соответствует |
|---|---|---|---|---|
\d |
Любая цифра | [0-9] |
\d\d |
"42" |
\D |
Любой не-цифра | [^0-9] |
\D+ |
"abc" |
\w |
Символ слова | [a-zA-Z0-9_] |
\w+ |
"привет_123" |
\W |
Не-символ слова | [^a-zA-Z0-9_] |
\W |
"@", "#" |
\s |
Пробельный символ | [ \t\n\r\f\v] |
\s+ |
" " (пробелы) |
\S |
Не-пробельный | [^ \t\n\r\f\v] |
\S+ |
"привет" |
. |
Любой символ кроме новой строки | - | a.c |
"abc", "a1c" |
Совет профи: \w НЕ включает Unicode-буквы по умолчанию. Используйте \p{L} для поддержки Unicode (JavaScript/Python).
Пользовательские классы символов
| Паттерн | Описание | Пример | Соответствует |
|---|---|---|---|
[abc] |
Соответствует любому из a, b или c | [аеиоу] |
Гласные: "а", "е", "и", "о", "у" |
[^abc] |
Соответствует любому кроме a, b или c | [^0-9] |
Не-цифры |
[a-z] |
Диапазон: строчные буквы | [a-z]+ |
"привет" |
[A-Z] |
Диапазон: заглавные буквы | [A-Z]+ |
"ПРИВЕТ" |
[0-9] |
Диапазон: цифры | [0-9]{4} |
"2025" |
[a-zA-Z] |
Комбинированный: все буквы | [a-zA-Z0-9] |
Буквенно-цифровой |
Примеры:
[аеиоу] → Соответствует любой гласной
[^аеиоу] → Соответствует любой согласной (не гласной)
[a-z0-9] → Соответствует строчным буквам и цифрам
[a-zA-Z0-9_] → То же, что \w (символы слова)
Квантификаторы
Квантификаторы определяют сколько раз должен встречаться паттерн.
Базовые квантификаторы
| Паттерн | Описание | Пример | Соответствует |
|---|---|---|---|
* |
0 или более | ab*c |
"ac", "abc", "abbc", "abbbc" |
+ |
1 или более | ab+c |
"abc", "abbc" (НЕ "ac") |
? |
0 или 1 (необязательно) | colou?r |
"color", "colour" |
{n} |
Ровно n раз | \d{4} |
"2025" (ровно 4 цифры) |
{n,} |
n или более раз | \d{2,} |
"42", "123", "9999" |
{n,m} |
От n до m раз | \d{2,4} |
"42", "123", "2025" |
Жадные vs. Ленивые квантификаторы
Жадные (по умолчанию): Соответствуют максимально возможному
<.*> → Соответствует: "<div>Привет</div>" (вся строка)
Ленивые (нежадные): Соответствуют минимально возможному (добавьте ? после квантификатора)
<.*?> → Соответствует: "<div>" и "</div>" отдельно
| Жадный | Ленивый | Описание |
|---|---|---|
* |
*? |
0 или более (ленивый) |
+ |
+? |
1 или более (ленивый) |
? |
?? |
0 или 1 (ленивый) |
{n,m} |
{n,m}? |
От n до m (ленивый) |
Пример:
Текст: "Привет" и "Мир"
".*"соответствует:"Привет" и "Мир"(жадный)".*?"соответствует:"Привет"и"Мир"отдельно (ленивый)
Якоря и границы
Якоря соответствуют позициям, а не символам.
| Паттерн | Описание | Пример | Соответствует |
|---|---|---|---|
^ |
Начало строки/линии | ^Привет |
"Привет Мир" (в начале) |
$ |
Конец строки/линии | Мир$ |
"Привет Мир" (в конце) |
\b |
Граница слова | \bкот\b |
"кот" в "Этот кот сидит" (НЕ "котёнок") |
\B |
Не-граница слова | \Bкот |
"котёнок" (кот НЕ на границе) |
\A |
Начало строки (не линии) | \AПривет |
Соответствует только если "Привет" в самом начале |
\z |
Конец строки (не линии) | Мир\z |
Соответствует только если "Мир" в самом конце |
\Z |
Конец строки (перед финальным переводом строки) | Мир\Z |
Соответствует "Мир" или "Мир\n" |
Примеры:
^кот$ → Соответствует: "кот" (вся строка это "кот")
\bкот\b → Соответствует: "кот" в "этот кот сидит" (целое слово)
\Bкот → Соответствует: "кот" в "котёнок" (НЕ на границе)
Многострочный режим (флаг m):
- Без
m:^и$соответствуют началу/концу всей строки - С
m:^и$соответствуют началу/концу каждой линии
Группы и альтернация
Захватывающие группы
Захватывающие группы (...) запоминают соответствующий текст:
(\d+)-(\d+) → Соответствует: "123-456"
Группа 1: "123"
Группа 2: "456"
Обратные ссылки (повторное использование захваченных групп):
(\w)\1 → Соответствует: "аа", "бб", "вв" (повторяющийся символ)
(\w+) \1 → Соответствует: "привет привет" (повторяющееся слово)
Незахватывающие группы
Используйте (?:...) когда нужна группировка, но не нужен захват:
(?:https?://) → Группирует "http://" или "https://" без захвата
Зачем использовать незахватывающие группы?
- Лучшая производительность (нет затрат памяти)
- Более чистые обратные ссылки (нумерованные группы считают только захватывающие)
Альтернация (ИЛИ)
Используйте | для "соответствует этому ИЛИ тому":
кот|собака → Соответствует: "кот" или "собака"
кофе(йн(ая|ый|ое)|) → Соответствует: "кофейная", "кофейный", "кофейное", "кофе"
Примеры:
(Г-н|Г-жа|Д-р)\.? → Соответствует: "Г-н.", "Г-жа", "Д-р."
https?:// → Соответствует: "http://" или "https://"
Утверждения Lookaround
Lookaround — это утверждения нулевой ширины, которые соответствуют позиции (как якоря), но с условиями.
Позитивный Lookahead (?=...)
Соответствует, если паттерн впереди совпадает (но не поглощает его):
\d(?=px) → Соответствует: "10" в "10px" (НЕ часть "px")
Пример использования: Валидация пароля
^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&]).{8,}$
Разбор:
(?=.*[A-Z])- Должна быть заглавная буква(?=.*[a-z])- Должна быть строчная буква(?=.*\d)- Должна быть цифра(?=.*[@$!%*?&])- Должен быть спецсимвол.{8,}- Минимум 8 символов
Негативный Lookahead (?!...)
Соответствует, если паттерн впереди НЕ совпадает:
\d(?!px) → Соответствует: "10" в "10em" (НЕ "10px")
Пример использования: Исключить определённые слова
\b(?!тест)\w+ → Соответствует словам, которые НЕ начинаются с "тест"
Позитивный Lookbehind (?<=...)
Соответствует, если паттерн позади совпадает:
(?<=₽)\d+ → Соответствует: "100" в "₽100" (НЕ часть "₽")
Пример использования: Извлечение цен
(?<=Цена: ₽)\d+,\d{2} → Соответствует: "29,99" в "Цена: ₽29,99"
Негативный Lookbehind (?<!...)
Соответствует, если паттерн позади НЕ совпадает:
(?<!₽)\d+ → Соответствует: "100", но НЕ в "₽100"
Сводная таблица:
| Тип | Синтаксис | Описание | Пример |
|---|---|---|---|
| Позитивный Lookahead | (?=...) |
Соответствует если за ним следует... | q(?=u) соответствует "q" в "queen" |
| Негативный Lookahead | (?!...) |
Соответствует если за ним НЕ следует... | q(?!u) соответствует "q" в "iraq" |
| Позитивный Lookbehind | (?<=...) |
Соответствует если перед ним... | (?<=₽)\d+ соответствует "10" в "₽10" |
| Негативный Lookbehind | (?<!...) |
Соответствует если перед ним НЕ... | (?<!₽)\d+ соответствует "10" в "10" |
Именованные захватывающие группы
Именованные группы (?<имя>...) делают regex более читаемыми:
JavaScript:
const dateRegex = /(?<год>\d{4})-(?<месяц>\d{2})-(?<день>\d{2})/;
const match = '2025-01-17'.match(dateRegex);
console.log(match.groups.год); // "2025"
console.log(match.groups.месяц); // "01"
console.log(match.groups.день); // "17"
Python:
import re
pattern = r'(?P<год>\d{4})-(?P<месяц>\d{2})-(?P<день>\d{2})'
match = re.search(pattern, '2025-01-17')
print(match.group('год')) # "2025"
print(match.group('месяц')) # "01"
print(match.group('день')) # "17"
C#:
var pattern = @"(?<год>\d{4})-(?<месяц>\d{2})-(?<день>\d{2})";
var match = Regex.Match("2025-01-17", pattern);
Console.WriteLine(match.Groups["год"].Value); // "2025"
Атомарные группы и притяжательные квантификаторы
Атомарные группы (?>...)
После соответствия группа не откатывается назад. Предотвращает катастрофический откат:
(?>\d+)bar → Соответствует: "123bar" (быстро)
Без атомарной группы:
\d+bar → Пробует: "123bar", "12bar", "1bar" (медленно при несоответствии)
Притяжательные квантификаторы
| Жадный | Притяжательный | Описание |
|---|---|---|
* |
*+ |
0 или более (без отката) |
+ |
++ |
1 или более (без отката) |
? |
?+ |
0 или 1 (без отката) |
Пример использования: Предотвращение катастрофического отката на сложных паттернах.
Поддержка Unicode
Современные движки regex поддерживают категории и скрипты Unicode.
Категории Unicode \p{...}
JavaScript (ES2018+):
const letters = /\p{L}+/u; // Любая буква (любой язык)
const numbers = /\p{N}+/u; // Любое число
const currency = /\p{Sc}/u; // Символы валюты
Python:
import regex # Примечание: требуется модуль 'regex', не 're'
letters = regex.compile(r'\p{L}+')
Распространённые категории Unicode:
| Категория | Описание | Пример |
|---|---|---|
\p{L} |
Буква | "a", "字", "א" |
\p{N} |
Число | "1", "①", "一" |
\p{S} |
Символ | "$", "©", "♥" |
\p{Sc} |
Символ валюты | "$", "€", "¥", "₽" |
\p{P} |
Пунктуация | ".", "!", "?" |
\p{Z} |
Разделитель | Пробел, табуляция |
Скрипты Unicode:
/\p{Script=Greek}/u → Соответствует греческим буквам: "α", "β", "γ"
/\p{Script=Cyrillic}/u → Соответствует кириллице: "а", "б", "в"
/\p{Script=Han}/u → Соответствует китайским символам
Отрицание:
/\P{L}+/u → Соответствует всему, что НЕ буква
Модификаторы и флаги
Флаги изменяют способ интерпретации паттернов regex.
| Флаг | Название | Описание | Пример |
|---|---|---|---|
i |
Без учёта регистра | Игнорирует регистр | /привет/i соответствует "Привет" |
g |
Глобальный | Находит все соответствия | /кот/g находит все "кот" |
m |
Многострочный | ^ и $ соответствуют началу/концу линии |
/^привет/m |
s |
Dotall | . соответствует также новой строке |
/a.b/s соответствует "a\nb" |
u |
Unicode | Включает функции Unicode | /\p{L}+/u |
x |
Расширенный | Игнорирует пробелы (free-spacing) | Позволяет комментарии |
y |
Sticky | Соответствует на точной позиции | Только JavaScript |
Примеры:
Без учёта регистра (i):
/привет/i.test('ПРИВЕТ') // true
Глобальный (g):
'кот собака кот'.match(/кот/g) // ["кот", "кот"]
Многострочный (m):
const text = 'Строка 1\nСтрока 2';
/^Строка 2/m.test(text) // true (без 'm': false)
Dotall (s):
/a.b/s.test('a\nb') // true (без 's': false)
Встроенные модификаторы
Применить флаги к части паттерна:
(?i)привет → "привет" без учёта регистра
(?-i)МИР → "МИР" с учётом регистра
(?i:привет) → Только "привет" без учёта регистра
Условные паттерны
Синтаксис: (?(условие)истина|ложь)
Пример: Соответствие строкам в кавычках или без
("|')?[^"'\r\n]*(?(1)\1)
Разбор:
("|')?- Необязательный захват открывающей кавычки[^"'\r\n]*- Соответствует содержимому(?(1)\1)- Если группа 1 совпала (открывающая кавычка), соответствует той же закрывающей кавычке
Соответствует:
"привет"✅'мир'✅тест✅ (без кавычек)"смешанное'❌ (несоответствующие кавычки)
Комментарии в Regex
Встроенные комментарии (?# комментарий)
\d{3}(?# код города)-\d{3}(?# центральная часть)-\d{2}(?# номер)-\d{2}
Режим Free-Spacing (флаг x)
Игнорирует пробелы и разрешает комментарии:
(?x)
\d{3} # код города
- # разделитель
\d{3} # центральная часть
- # разделитель
\d{2} # номер
- # разделитель
\d{2} # номер
Гораздо читабельнее для сложных паттернов!
Этот раздел демонстрирует, как использовать regex в 7 популярных языках программирования. У каждого языка свой API для regex, но синтаксис паттернов остаётся в основном одинаковым.
JavaScript / Node.js
Создание Regex паттернов
// Литеральная нотация (наиболее распространённая)
const pattern1 = /\d{3}-\d{4}/;
// Конструктор (когда паттерн динамический)
const pattern2 = new RegExp('\\d{3}-\\d{4}');
// Примечание: Обратные слеши должны быть экранированы в строках
// С флагами
const pattern3 = /привет/gi; // Глобальный, без учёта регистра
Методы String
// .match() - Находит соответствия
const text = 'Контакт: 123-4567 или 987-6543';
const matches = text.match(/\d{3}-\d{4}/g);
console.log(matches); // ["123-4567", "987-6543"]
// .matchAll() - Получает все соответствия с группами (ES2020)
const emailPattern = /([\w.-]+)@([\w.-]+\.[a-z]{2,})/gi;
const emails = 'admin@example.ru, user@test.org';
for (const match of emails.matchAll(emailPattern)) {
console.log(`Пользователь: ${match[1]}, Домен: ${match[2]}`);
}
// Пользователь: admin, Домен: example.ru
// Пользователь: user, Домен: test.org
// .search() - Находит позицию первого соответствия
const pos = 'Привет Мир'.search(/Мир/);
console.log(pos); // 7
// .replace() - Заменяет соответствия
const phone = '(+7) 916 123 45 67';
const cleaned = phone.replace(/[^\d]/g, '');
console.log(cleaned); // "79161234567"
// .replaceAll() - Заменяет все соответствия (ES2021)
const text2 = 'кот собака кот';
const result = text2.replaceAll(/кот/g, 'птица');
console.log(result); // "птица собака птица"
// .split() - Разделяет по паттерну
const csv = 'яблоко,банан, апельсин , виноград';
const fruits = csv.split(/\s*,\s*/);
console.log(fruits); // ["яблоко", "банан", "апельсин", "виноград"]
Методы RegExp
// .test() - Возвращает boolean
const isEmail = /^[\w.-]+@[\w.-]+\.[a-z]{2,}$/i;
console.log(isEmail.test('user@example.ru')); // true
// .exec() - Возвращает детали соответствия (или null)
const pattern = /(\d{4})-(\d{2})-(\d{2})/;
const match = pattern.exec('Дата: 2025-01-17');
if (match) {
console.log(match[0]); // "2025-01-17" (полное соответствие)
console.log(match[1]); // "2025" (группа 1)
console.log(match[2]); // "01" (группа 2)
console.log(match[3]); // "17" (группа 3)
}
Именованные группы (ES2018+)
const pattern = /(?<год>\d{4})-(?<месяц>\d{2})-(?<день>\d{2})/;
const match = '2025-01-17'.match(pattern);
console.log(match.groups.год); // "2025"
console.log(match.groups.месяц); // "01"
console.log(match.groups.день); // "17"
// Именованные обратные ссылки
const dupeWord = /\b(?<слово>\w+)\s+\k<слово>\b/i;
console.log(dupeWord.test('привет привет')); // true
Поддержка Unicode (ES2018+)
// Соответствует любой букве (включая кириллицу, китайский, арабский и т.д.)
const letters = /\p{L}+/u;
console.log(letters.test('кофе')); // true
console.log(letters.test('你好')); // true
// Соответствует эмодзи
const emoji = /\p{Emoji}/u;
console.log(emoji.test('Привет 👋')); // true
Python
Модуль re
import re
# Компиляция паттерна (рекомендуется для повторного использования)
pattern = re.compile(r'\d{3}-\d{4}')
# Или использовать напрямую
re.search(r'\d{3}-\d{4}', 'Звоните 123-4567')
Основные функции
import re
# re.search() - Находит первое соответствие
match = re.search(r'\d{3}-\d{4}', 'Контакт: 123-4567 или 987-6543')
if match:
print(match.group()) # "123-4567"
print(match.start()) # 9 (позиция)
print(match.end()) # 17
# re.match() - Соответствует в НАЧАЛЕ строки
match = re.match(r'\d+', '123 улица Ленина')
print(match.group() if match else None) # "123"
match = re.match(r'\d+', 'улица Ленина 123')
print(match) # None (не начинается с цифры)
# re.fullmatch() - Соответствует ВСЕЙ строке
result = re.fullmatch(r'\d{3}-\d{4}', '123-4567')
print(bool(result)) # True
result = re.fullmatch(r'\d{3}-\d{4}', 'Звоните 123-4567')
print(bool(result)) # False (дополнительный текст)
# re.findall() - Находит все соответствия (возвращает список)
text = 'Цены: ₽10, ₽25, ₽100'
prices = re.findall(r'₽(\d+)', text)
print(prices) # ['10', '25', '100']
# re.finditer() - Находит все соответствия (возвращает итератор)
for match in re.finditer(r'₽(\d+)', text):
print(f'Найдено ₽{match.group(1)} на позиции {match.start()}')
# Найдено ₽10 на позиции 7
# Найдено ₽25 на позиции 12
# Найдено ₽100 на позиции 17
# re.sub() - Заменяет соответствия
phone = '(+7) 916 123 45 67'
cleaned = re.sub(r'[^\d]', '', phone)
print(cleaned) # "79161234567"
# re.split() - Разделяет по паттерну
csv = 'яблоко,банан, апельсин , виноград'
fruits = re.split(r'\s*,\s*', csv)
print(fruits) # ['яблоко', 'банан', 'апельсин', 'виноград']
Группы и именованные группы
import re
# Нумерованные группы
pattern = r'(\d{4})-(\d{2})-(\d{2})'
match = re.search(pattern, 'Дата: 2025-01-17')
if match:
print(match.group(0)) # "2025-01-17" (полное соответствие)
print(match.group(1)) # "2025"
print(match.group(2)) # "01"
print(match.group(3)) # "17"
print(match.groups()) # ('2025', '01', '17')
# Именованные группы (?P<имя>...)
pattern = r'(?P<год>\d{4})-(?P<месяц>\d{2})-(?P<день>\d{2})'
match = re.search(pattern, '2025-01-17')
if match:
print(match.group('год')) # "2025"
print(match.group('месяц')) # "01"
print(match.group('день')) # "17"
print(match.groupdict()) # {'год': '2025', 'месяц': '01', 'день': '17'}
Флаги
import re
# Без учёта регистра
re.search(r'привет', 'ПРИВЕТ', re.IGNORECASE) # или re.I
# Многострочный (^ и $ соответствуют началу/концу линии)
re.search(r'^Строка 2', 'Строка 1\nСтрока 2', re.MULTILINE) # или re.M
# Dotall (. соответствует новой строке)
re.search(r'a.b', 'a\nb', re.DOTALL) # или re.S
# Verbose (режим free-spacing с комментариями)
pattern = re.compile(r'''
\d{3} # код города
- # разделитель
\d{3} # центральная часть
- # разделитель
\d{4} # номер
''', re.VERBOSE) # или re.X
# Комбинирование флагов с |
pattern = re.compile(r'привет', re.IGNORECASE | re.MULTILINE)
Замена с помощью функций
import re
# Использование функции для динамических замен
def double_number(match):
num = int(match.group())
return str(num * 2)
text = 'У меня 5 яблок и 10 апельсинов'
result = re.sub(r'\d+', double_number, text)
print(result) # "У меня 10 яблок и 20 апельсинов"
# С именованными группами
def format_name(match):
return f"{match.group('фамилия').upper()}, {match.group('имя')}"
pattern = r'(?P<имя>\w+)\s+(?P<фамилия>\w+)'
text = 'Иван Петров'
result = re.sub(pattern, format_name, text)
print(result) # "ПЕТРОВ, Иван"
PHP
Функции PCRE
<?php
// preg_match() - Находит первое соответствие
$pattern = '/\d{3}-\d{4}/';
$text = 'Контакт: 123-4567 или 987-6543';
if (preg_match($pattern, $text, $matches)) {
echo $matches[0]; // "123-4567"
}
// preg_match_all() - Находит все соответствия
preg_match_all('/\d{3}-\d{4}/', $text, $matches);
print_r($matches[0]); // ["123-4567", "987-6543"]
// preg_replace() - Заменяет соответствия
$phone = '(+7) 916 123 45 67';
$cleaned = preg_replace('/[^\d]/', '', $phone);
echo $cleaned; // "79161234567"
// preg_split() - Разделяет по паттерну
$csv = 'яблоко,банан, апельсин , виноград';
$fruits = preg_split('/\s*,\s*/', $csv);
print_r($fruits); // ["яблоко", "банан", "апельсин", "виноград"]
// preg_grep() - Фильтрует массив по паттерну
$words = ['яблоко', 'банан', 'абрикос', 'апельсин'];
$aWords = preg_grep('/^а/', $words);
print_r($aWords); // ["абрикос", "апельсин"]
?>
Именованные группы
<?php
$pattern = '/(?P<год>\d{4})-(?P<месяц>\d{2})-(?P<день>\d{2})/';
$text = '2025-01-17';
if (preg_match($pattern, $text, $matches)) {
echo $matches['год']; // "2025"
echo $matches['месяц']; // "01"
echo $matches['день']; // "17"
}
?>
Модификаторы (флаги)
<?php
// i - Без учёта регистра
preg_match('/привет/i', 'ПРИВЕТ'); // Соответствует
// m - Многострочный
preg_match('/^Строка 2/m', "Строка 1\nСтрока 2"); // Соответствует
// s - Dotall (. соответствует новой строке)
preg_match('/a.b/s', "a\nb"); // Соответствует
// x - Free-spacing (игнорирует пробелы)
$pattern = '/
\d{3} # код города
- # разделитель
\d{3} # центральная часть
- # разделитель
\d{4} # номер
/x';
// u - Поддержка UTF-8
preg_match('/\w+/u', 'кофе'); // Соответствует (включает кириллицу)
// Комбинирование модификаторов
preg_match('/привет/imu', $text);
?>
Замена с помощью callback
<?php
$text = 'У меня 5 яблок и 10 апельсинов';
$result = preg_replace_callback('/\d+/', function($matches) {
return (int)$matches[0] * 2;
}, $text);
echo $result; // "У меня 10 яблок и 20 апельсинов"
?>
C# (.NET)
Класс Regex
using System;
using System.Text.RegularExpressions;
// Статические методы (простое использование)
string text = "Контакт: 123-4567 или 987-6543";
Match match = Regex.Match(text, @"\d{3}-\d{4}");
if (match.Success)
{
Console.WriteLine(match.Value); // "123-4567"
}
// Находит все соответствия
MatchCollection matches = Regex.Matches(text, @"\d{3}-\d{4}");
foreach (Match m in matches)
{
Console.WriteLine(m.Value);
}
// Вывод:
// 123-4567
// 987-6543
// Заменяет
string phone = "(+7) 916 123 45 67";
string cleaned = Regex.Replace(phone, @"[^\d]", "");
Console.WriteLine(cleaned); // "79161234567"
// Разделяет
string csv = "яблоко,банан, апельсин , виноград";
string[] fruits = Regex.Split(csv, @"\s*,\s*");
// ["яблоко", "банан", "апельсин", "виноград"]
Скомпилированный Regex (лучшая производительность)
using System.Text.RegularExpressions;
// Компилирует для повторного использования (намного быстрее)
Regex pattern = new Regex(@"\d{3}-\d{4}", RegexOptions.Compiled);
string text = "Контакт: 123-4567";
Match match = pattern.Match(text);
if (match.Success)
{
Console.WriteLine(match.Value);
}
RegexOptions (флаги)
using System.Text.RegularExpressions;
// Без учёта регистра
Regex.IsMatch("ПРИВЕТ", "привет", RegexOptions.IgnoreCase);
// Многострочный
Regex.Match("Строка 1\nСтрока 2", "^Строка 2", RegexOptions.Multiline);
// Singleline (. соответствует новой строке)
Regex.Match("a\nb", "a.b", RegexOptions.Singleline);
// Скомпилированный (лучшая производительность)
var pattern = new Regex(@"\d+", RegexOptions.Compiled);
// Комбинирование опций
var opts = RegexOptions.IgnoreCase | RegexOptions.Multiline;
Regex.Match(text, pattern, opts);
Именованные группы
using System;
using System.Text.RegularExpressions;
string pattern = @"(?<год>\d{4})-(?<месяц>\d{2})-(?<день>\d{2})";
Match match = Regex.Match("2025-01-17", pattern);
if (match.Success)
{
Console.WriteLine(match.Groups["год"].Value); // "2025"
Console.WriteLine(match.Groups["месяц"].Value); // "01"
Console.WriteLine(match.Groups["день"].Value); // "17"
}
Замена с помощью MatchEvaluator
using System;
using System.Text.RegularExpressions;
string text = "У меня 5 яблок и 10 апельсинов";
string result = Regex.Replace(text, @"\d+", match =>
{
int num = int.Parse(match.Value);
return (num * 2).ToString();
});
Console.WriteLine(result); // "У меня 10 яблок и 20 апельсинов"
Java
Классы Pattern и Matcher
import java.util.regex.Pattern;
import java.util.regex.Matcher;
// Компилирует паттерн
Pattern pattern = Pattern.compile("\\d{3}-\\d{4}");
String text = "Контакт: 123-4567 или 987-6543";
// Создаёт matcher
Matcher matcher = pattern.matcher(text);
// Находит первое соответствие
if (matcher.find()) {
System.out.println(matcher.group()); // "123-4567"
}
// Находит все соответствия
matcher.reset(); // Сбрасывает в начало
while (matcher.find()) {
System.out.println(matcher.group());
}
// Вывод:
// 123-4567
// 987-6543
Методы String
// matches() - Проверяет соответствие ВСЕЙ строки
boolean isPhone = "123-4567".matches("\\d{3}-\\d{4}");
System.out.println(isPhone); // true
// replaceAll() - Заменяет все соответствия
String phone = "(+7) 916 123 45 67";
String cleaned = phone.replaceAll("[^\\d]", "");
System.out.println(cleaned); // "79161234567"
// replaceFirst() - Заменяет первое соответствие
String text = "кот собака кот";
String result = text.replaceFirst("кот", "птица");
System.out.println(result); // "птица собака кот"
// split() - Разделяет по паттерну
String csv = "яблоко,банан, апельсин , виноград";
String[] fruits = csv.split("\\s*,\\s*");
// ["яблоко", "банан", "апельсин", "виноград"]
Флаги Pattern
import java.util.regex.Pattern;
// Без учёта регистра
Pattern pattern = Pattern.compile("привет", Pattern.CASE_INSENSITIVE);
// Многострочный
Pattern.compile("^Строка 2", Pattern.MULTILINE);
// Dotall (. соответствует новой строке)
Pattern.compile("a.b", Pattern.DOTALL);
// Комментарии (free-spacing)
Pattern.compile("""
\\d{3} # код города
- # разделитель
\\d{3} # центральная часть
- # разделитель
\\d{4} # номер
""", Pattern.COMMENTS);
// Комбинирование флагов
int flags = Pattern.CASE_INSENSITIVE | Pattern.MULTILINE;
Pattern.compile("pattern", flags);
Именованные группы
import java.util.regex.Pattern;
import java.util.regex.Matcher;
Pattern pattern = Pattern.compile("(?<год>\\d{4})-(?<месяц>\\d{2})-(?<день>\\d{2})");
Matcher matcher = pattern.matcher("2025-01-17");
if (matcher.find()) {
System.out.println(matcher.group("год")); // "2025"
System.out.println(matcher.group("месяц")); // "01"
System.out.println(matcher.group("день")); // "17"
}
Продвинутая замена
import java.util.regex.Pattern;
import java.util.regex.Matcher;
String text = "У меня 5 яблок и 10 апельсинов";
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(text);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
int num = Integer.parseInt(matcher.group());
matcher.appendReplacement(result, String.valueOf(num * 2));
}
matcher.appendTail(result);
System.out.println(result); // "У меня 10 яблок и 20 апельсинов"
Go (Golang)
Пакет regexp
package main
import (
"fmt"
"regexp"
)
func main() {
// Компилирует паттерн
pattern := regexp.MustCompile(`\d{3}-\d{4}`)
text := "Контакт: 123-4567 или 987-6543"
// Находит первое соответствие
match := pattern.FindString(text)
fmt.Println(match) // "123-4567"
// Находит все соответствия
matches := pattern.FindAllString(text, -1)
fmt.Println(matches) // [123-4567 987-6543]
// Проверяет соответствие
isMatch := pattern.MatchString("123-4567")
fmt.Println(isMatch) // true
// Заменяет всё
phone := "(+7) 916 123 45 67"
cleaned := regexp.MustCompile(`[^\d]`).ReplaceAllString(phone, "")
fmt.Println(cleaned) // "79161234567"
// Разделяет
csv := "яблоко,банан, апельсин , виноград"
fruits := regexp.MustCompile(`\s*,\s*`).Split(csv, -1)
fmt.Println(fruits) // [яблоко банан апельсин виноград]
}
Submatch (группы)
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`)
text := "Дата: 2025-01-17"
// FindStringSubmatch возвращает [полное, группа1, группа2, ...]
matches := pattern.FindStringSubmatch(text)
if matches != nil {
fmt.Println(matches[0]) // "2025-01-17" (полное соответствие)
fmt.Println(matches[1]) // "2025" (группа 1)
fmt.Println(matches[2]) // "01" (группа 2)
fmt.Println(matches[3]) // "17" (группа 3)
}
// FindAllStringSubmatch для всех соответствий
text2 := "Даты: 2025-01-17 и 2024-12-31"
allMatches := pattern.FindAllStringSubmatch(text2, -1)
for _, match := range allMatches {
fmt.Printf("Год: %s, Месяц: %s, День: %s\n", match[1], match[2], match[3])
}
// Год: 2025, Месяц: 01, День: 17
// Год: 2024, Месяц: 12, День: 31
}
Именованные группы
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := regexp.MustCompile(`(?P<год>\d{4})-(?P<месяц>\d{2})-(?P<день>\d{2})`)
text := "2025-01-17"
match := pattern.FindStringSubmatch(text)
if match != nil {
// Получает индексы именованных групп
names := pattern.SubexpNames()
result := make(map[string]string)
for i, name := range names {
if i != 0 && name != "" {
result[name] = match[i]
}
}
fmt.Println(result["год"]) // "2025"
fmt.Println(result["месяц"]) // "01"
fmt.Println(result["день"]) // "17"
}
}
Замена с помощью функций
package main
import (
"fmt"
"regexp"
"strconv"
)
func main() {
text := "У меня 5 яблок и 10 апельсинов"
pattern := regexp.MustCompile(`\d+`)
result := pattern.ReplaceAllStringFunc(text, func(s string) string {
num, _ := strconv.Atoi(s)
return strconv.Itoa(num * 2)
})
fmt.Println(result) // "У меня 10 яблок и 20 апельсинов"
}
Ruby
Литералы Regex
# Литеральная нотация
pattern = /\d{3}-\d{4}/
# С флагами
pattern_ci = /привет/i # Без учёта регистра
pattern_multi = /^строка/m # Многострочный
# Конструктор (для динамических паттернов)
pattern = Regex.new('\d{3}-\d{4}')
Методы String
text = 'Контакт: 123-4567 или 987-6543'
# match() - Возвращает MatchData или nil
match = text.match(/\d{3}-\d{4}/)
if match
puts match[0] # "123-4567"
end
# scan() - Находит все соответствия
matches = text.scan(/\d{3}-\d{4}/)
puts matches # ["123-4567", "987-6543"]
# =~ оператор - Возвращает индекс первого соответствия
index = text =~ /\d{3}-\d{4}/
puts index # 9
# sub() - Заменяет первое соответствие
result = 'кот собака кот'.sub(/кот/, 'птица')
puts result # "птица собака кот"
# gsub() - Заменяет все соответствия
phone = '(+7) 916 123 45 67'
cleaned = phone.gsub(/[^\d]/, '')
puts cleaned # "79161234567"
# split() - Разделяет по паттерну
csv = 'яблоко,банан, апельсин , виноград'
fruits = csv.split(/\s*,\s*/)
puts fruits # ["яблоко", "банан", "апельсин", "виноград"]
Захватывающие группы
pattern = /(\d{4})-(\d{2})-(\d{2})/
match = '2025-01-17'.match(pattern)
if match
puts match[0] # "2025-01-17" (полное соответствие)
puts match[1] # "2025" (группа 1)
puts match[2] # "01" (группа 2)
puts match[3] # "17" (группа 3)
end
Именованные группы
pattern = /(?<год>\d{4})-(?<месяц>\d{2})-(?<день>\d{2})/
match = '2025-01-17'.match(pattern)
if match
puts match[:год] # "2025"
puts match[:месяц] # "01"
puts match[:день] # "17"
end
Замена с помощью блоков
text = 'У меня 5 яблок и 10 апельсинов'
result = text.gsub(/\d+/) { |num| (num.to_i * 2).to_s }
puts result # "У меня 10 яблок и 20 апельсинов"
# С именованными группами
pattern = /(?<имя>\w+)\s+(?<фамилия>\w+)/
text = 'Иван Петров'
result = text.gsub(pattern) do |match|
m = Regexp.last_match
"#{m[:фамилия].upcase}, #{m[:имя]}"
end
puts result # "ПЕТРОВ, Иван"
Флаги
# i - Без учёта регистра
/привет/i.match('ПРИВЕТ') # Соответствует
# m - Многострочный (. соответствует новой строке)
/a.b/m.match("a\nb") # Соответствует
# x - Free-spacing (игнорирует пробелы)
pattern = /
\d{3} # код города
- # разделитель
\d{3} # центральная часть
- # разделитель
\d{4} # номер
/x
# o - Компилирует один раз (оптимизация)
pattern = /\d+/o
Поиск и замена в Visual Studio Code (Ctrl/Cmd+H) поддерживает regex с мощными возможностями трансформации. Этот раздел показывает 28 практических примеров, которые разработчики используют каждый день.
Доступ к поиску и замене
Горячие клавиши:
- Поиск:
Ctrl+F(Windows/Linux) /Cmd+F(Mac) - Замена:
Ctrl+H(Windows/Linux) /Cmd+H(Mac) - Включить Regex: Нажмите кнопку
.*или нажмитеAlt+R
Советы:
- Используйте
Ctrl+Enter(Cmd+Enter) для замены всех - Предпросмотр соответствий перед заменой (подсвечиваются жёлтым)
- Используйте
F3/Shift+F3для навигации между соответствиями
Трансформации регистра
VS Code поддерживает специальные последовательности замены для преобразования регистра:
| Последовательность | Эффект | Пример |
|---|---|---|
\l |
Строчная следующая буква | \l |
\u |
Заглавная следующая буква | \u |
\L |
Строчные все последующие буквы | \L |
\U |
Заглавные все последующие буквы | \U |
\E |
Завершить трансформацию регистра | \U\E |
Пример 1: Первая буква заглавная
Найти:
\b(\w)(\w*)
Заменить:
\u$1$2
До:
привет мир
После:
Привет Мир
Этот раздел предоставляет более 25 готовых к использованию regex паттернов.
Валидация Email
^[\w.-]+@[\w.-]+\.[a-z]{2,}$
Номера телефонов (Россия)
^(\+?7)?[-.\s]?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{2})[-.\s]?([0-9]{2})$
Даты (ISO 8601)
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
URL
^https?://(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b
IPv4 адреса
^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$
Цвета Hex
^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$
Надёжный пароль
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$
Имя пользователя (3-16 символов)
^[a-zA-Z0-9_-]{3,16}$
UUID v4
^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
И ещё 15+ паттернов с полной документацией!
Применение regex в реальной разработке.
Валидация форм
const validators = {
email: /^[\w.-]+@[\w.-]+\.[a-z]{2,}$/i,
phone: /^\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{2}[-.\s]?\d{2}$/
};
Извлечение данных
Извлечение email из текста:
import re
emails = re.findall(r'[\w.-]+@[\w.-]+\.[a-z]{2,}', text)
Парсинг логов
pattern = r'(?P<ip>[\d.]+).+(?P<status>\d{3})'
Рефакторинг кода
Преобразование старых вызовов API в VS Code:
Найти: apiClient\.get\('([^']+)'\)
Заменить: fetch('$1').then(r => r.json())
Частые ошибки
1. Забыли экранировать спецсимволы
❌ Неправильно: file.txt
✅ Правильно: file\.txt
2. Жадный vs. Ленивый
❌ Жадный: <.*> соответствует всему <div>текст</div>
✅ Ленивый: <.*?> соответствует <div> и </div> отдельно
3. Не используются якоря
❌ /\d{3}/ соответствует "123" в "abc123def"
✅ /^\d{3}$/ соответствует только точно "123"
Советы по производительности
- Используйте конкретные классы символов вместо
. - Закрепляйте паттерны когда возможно
- Избегайте вложенных квантификаторов (катастрофический откат)
- Используйте атомарные группы для производительности
- Компилируйте паттерны для повторного использования
Отладка
- Тестируйте на regex101.com
- Используйте verbose режим с комментариями
- Разбивайте сложные паттерны на части
- Тестируйте граничные случаи
Онлайн-инструменты
Тестеры Regex
- regex101.com - Лучший тестер с объяснениями
- regexr.com - Визуальный конструктор regex
- regexpal.com - Простой и быстрый тест
Визуализаторы
- debuggex.com - Диаграммы railroad
- regexper.com - Визуальный инструмент
Обучающие ресурсы
- regexone.com - Интерактивные уроки
- regexlearn.com - Пошаговое руководство
- regular-expressions.info - Документация
Расширения IDE
- Regex Previewer (VS Code)
- Regex Tester (VS Code)
Классы символов
| Паттерн | Соответствует |
|---|---|
\d |
Цифра [0-9] |
\w |
Слово [a-zA-Z0-9_] |
\s |
Пробельный символ |
. |
Любой символ |
Квантификаторы
| Паттерн | Значение |
|---|---|
* |
0 или более |
+ |
1 или более |
? |
0 или 1 |
{n} |
Ровно n |
Якоря
| Паттерн | Значение |
|---|---|
^ |
Начало строки |
$ |
Конец строки |
\b |
Граница слова |
Флаги
| Флаг | Значение |
|---|---|
i |
Без учёта регистра |
g |
Глобальный |
m |
Многострочный |
s |
Dotall |
Общие вопросы
В: В чём разница между жадными и ленивыми квантификаторами?
О: Жадные соответствуют максимально возможному. Ленивые (*?, +?) соответствуют минимально возможному.
В: Как найти литеральную точку?
О: Экранируйте её обратным слешем: \.
В: Что такое катастрофический откат?
О: Когда regex пробует множество комбинаций, вызывая замедление. Избегайте вложенных квантификаторов типа (a+)+.
В: Могут ли regex идеально валидировать email?
О: Нет. Используйте regex для базового формата, затем проверяйте через email.
В: Как сопоставить на нескольких строках?
О: Используйте флаг s, или используйте [\s\S]* вместо .*.
Специфично для VS Code
В: Как заменить с изменением регистра в VS Code?
О: Используйте \u (заглавная следующая), \U (заглавные все).
В: Могу ли я использовать regex в поиске файлов VS Code?
О: Да! Нажмите Ctrl+Shift+F и включите regex (Alt+R).
Расширение Chrome Regex Data Extractor
Наш Regex Data Extractor помогает извлекать данные с веб-страниц используя паттерны из этого руководства.
Ключевые возможности
- Библиотека паттернов: Готовые паттерны
- Тестирование в реальном времени: Тестируйте regex на любой веб-странице
- Экспорт в разные форматы: CSV, JSON, Excel, PDF
- Пакетное извлечение: Извлекайте из нескольких страниц
Пример: Извлечение Email
- Установите Regex Data Extractor
- Перейдите на любую веб-страницу
- Нажмите на иконку расширения
- Введите паттерн:
[\w.-]+@[\w.-]+\.[a-z]{2,} - Нажмите "Извлечь"
- Экспортируйте в CSV/JSON
Пример: Извлечение цен
Паттерн: ₽([0-9]{1,3}(?:\.?[0-9]{3})*,?[0-9]{2})
Находит: ₽1.234,56, ₽99,99
Профессиональные советы
- Сохраняйте часто используемые паттерны
- Используйте именованные группы для структурированных данных
- Тестируйте паттерны сначала
- Экспортируйте для анализа данных