Шпаргалка по Regex: Полное руководство по регулярным выражениям

Овладейте регулярными выражениями с помощью этого полного руководства. Изучите паттерны, синтаксис, реализации для разных языков (Python, JavaScript, PHP, C#, Java, Go, Ruby), трансформации поиска/замены в VS Code и более 200 практических примеров. Исчерпывающий ресурс по regex для новичков и экспертов.

G
GUi Softworks
60 мин чтения

Что такое Regex?

Регулярные выражения (regex или regexp) — это последовательности символов, которые определяют паттерны поиска. Они являются одним из самых мощных инструментов для обработки текста, распознавания паттернов и извлечения данных.

Почему использовать регулярные выражения?

Regex необходимы для:

  • Валидации форм (email, номера телефонов, пароли)
  • Извлечения данных (парсинг логов, скрейпинг веб-страниц)
  • Обработки текста (поиск и замена, форматирование)
  • Рефакторинга кода (переименование переменных, обновление синтаксиса)
  • Санитизации ввода (безопасность, предотвращение injection-атак)

Как читать эту шпаргалку

Это руководство организовано в прогрессивные разделы:

  1. Основы - Базовый синтаксис и паттерны
  2. Продвинутые возможности - Lookaround, именованные группы, Unicode
  3. Особенности языков - Примеры на Python, JavaScript, PHP, C#, Java, Go, Ruby
  4. Regex в VS Code - Трансформации поиска/замены с более чем 20 примерами
  5. Распространённые паттерны - Готовые паттерны для email, URL, дат и т.д.
  6. Практические примеры - Применение в реальном мире
  7. Устранение проблем - Частые ошибки и советы по производительности

Каждый раздел включает:

  • Понятные объяснения
  • Визуальные примеры
  • Готовые к копированию фрагменты кода
  • Профессиональные советы и подводные камни

Литеральные символы

Самый простой 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"

Советы по производительности

  1. Используйте конкретные классы символов вместо .
  2. Закрепляйте паттерны когда возможно
  3. Избегайте вложенных квантификаторов (катастрофический откат)
  4. Используйте атомарные группы для производительности
  5. Компилируйте паттерны для повторного использования

Отладка

  1. Тестируйте на regex101.com
  2. Используйте verbose режим с комментариями
  3. Разбивайте сложные паттерны на части
  4. Тестируйте граничные случаи

Онлайн-инструменты

Тестеры 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

  1. Установите Regex Data Extractor
  2. Перейдите на любую веб-страницу
  3. Нажмите на иконку расширения
  4. Введите паттерн: [\w.-]+@[\w.-]+\.[a-z]{2,}
  5. Нажмите "Извлечь"
  6. Экспортируйте в CSV/JSON

Пример: Извлечение цен

Паттерн: ₽([0-9]{1,3}(?:\.?[0-9]{3})*,?[0-9]{2})
Находит: ₽1.234,56, ₽99,99

Профессиональные советы

  • Сохраняйте часто используемые паттерны
  • Используйте именованные группы для структурированных данных
  • Тестируйте паттерны сначала
  • Экспортируйте для анализа данных

Получить Regex Data Extractor →

regexрегулярные выраженияшпаргалкапаттерныруководствоvs codepythonjavascriptphp

Last updated: 16 января 2025 г.