Skip to content
Назад к блогу
Руководства

Шпаргалка Regex: метасимволы, группы и lookaround

Шпаргалка regex: метасимволы, квантификаторы, якоря, группы, lookaround, 15+ паттернов для JavaScript и Python, разбор катастрофического возврата.

12 мин чтения

Шпаргалка Regex: метасимволы, группы и lookaround (полный справочник)

Регулярное выражение — это маленький язык шаблонов для поиска по тексту: \d+ значит «одна или более цифр», ^Error — «строка, начинающаяся с Error». Этим всё и ограничивается. Шпаргалка по регулярным выражениям (regex) собирает синтаксис на одной прокручиваемой странице: метасимволы, квантификаторы, якоря, группы, lookaround, флаги — и 15+ готовых паттернов под JavaScript и Python.

Текст написан для разработчиков, которые знают, что такое строка, и хотят справочник, а не экскурсию. Если нужны только символы — листайте сразу к таблице быстрого справочника. Если regex у вас хоть раз подвешивал сервер — читайте разделы про lookaround и подводные камни.

1. Что такое regex и почему он по-прежнему нужен в 2026 году

Regex — это паттерн, скомпилированный в конечный автомат: автомат сканирует строку и либо находит совпадение, либо отказывает. Грамматика небольшая, а применений у неё много.

ИИ набросает паттерн за секунду, но три задачи по-прежнему делаются вручную:

  • Разбор логов. Десять миллионов строк access-логов nginx, нужны все запросы с кодом 5xx от конкретного user agent. Regex из 40 символов через grep -E отработает за секунды; вызов LLM на каждую строку — нет.
  • Валидация форм и полей. Номера телефонов, почтовые индексы, ISO timestamp, лицензионные ключи. Паттерн живёт рядом с полем ввода и срабатывает на каждое нажатие клавиши в браузере.
  • Массовый поиск и замена. Рефакторинг тысячи файлов, где нужно захватить имя и переиспользовать его. sed, ripgrep, команда «Заменить в файлах» в редакторе — все они изначально говорят на regex.

Для JSON-половины того же набора инструментов есть отдельная шпаргалка по jq для командной строки.

1.1 Как читать regex-паттерн (краткое введение в регулярные выражения за 5 секунд)

Большинство паттернов проще читать слева направо, по одному токену. Возьмём для примера ^[A-Z]\w+\d{2,4}$:

  • ^ привязывает совпадение к началу строки.
  • [A-Z] совпадает ровно с одной заглавной буквой.
  • \w+ совпадает с одним или более символами слова.
  • \d{2,4} совпадает с числом цифр от двух до четырёх.
  • $ привязывает к концу строки.

Навык в том, чтобы читать сначала якоря, потом классы символов, и только потом квантификаторы.

2. Таблица быстрого справочника

Ради этого раздела сюда обычно и приходят. Копируйте нужное.

Метасимволы

ПаттернЧто соответствует
.Любой символ, кроме перевода строки (или любой символ с флагом s/dotall)
\dЦифра ([0-9] или все Unicode-цифры с флагом u)
\DНе-цифра
\wСимвол слова ([A-Za-z0-9_])
\WНесимвол слова
\sЛюбой пробельный символ (пробел, табуляция, перевод строки, …)
\SЛюбой непробельный символ

Квантификаторы

ПаттернЧто соответствует
*0 или более (жадный)
+1 или более (жадный)
?0 или 1 (жадный)
{n}Ровно n раз
{n,m}От n до m раз
{n,}n или более раз
*?, +?, ??, {n,m}?Ленивые версии каждого квантификатора

Якоря

ПаттернЧто соответствует
^Начало строки (или начало строки текста с флагом m)
$Конец строки (или конец строки текста с флагом m)
\bГраница слова
\BНе-граница слова
\AАбсолютное начало строки (Python)
\ZАбсолютный конец строки (Python)

Классы символов

ПаттернЧто соответствует
[abc]Любой из a, b, c
[^abc]Всё, кроме a, b, c
[a-z]Любая строчная буква
[0-9]Любая цифра
\p{L}Любая Unicode-буква (флаг u в JS, по умолчанию в Python re)

Группы

ПаттернЧто соответствует
(...)Захватывающая группа
(?:...)Незахватывающая группа
(?<name>...)Именованный захват (JS ES2018+); Python использует (?P<name>...)
\1, \2Обратная ссылка на группу 1, 2

Lookaround

ПаттернЧто соответствует
(?=...)Положительный lookahead
(?!...)Отрицательный lookahead
(?<=...)Положительный lookbehind
(?<!...)Отрицательный lookbehind

Флаги

ФлагЭффект
iБез учёта регистра
mMultiline: ^ и $ совпадают для каждой строки
sDotall: . совпадает с переводами строк
gGlobal (JS) — найти все совпадения
uРежим Unicode
ySticky (JS) — привязать к lastIndex

3. Метасимволы и классы символов

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

Большинство символов — это литералы. 12 метасимволов, которые нужно экранировать, чтобы они означали сами себя:

. ^ $ * + ? ( ) [ ] { } | \

Забытое экранирование . — самая частая ошибка в regex. \. совпадает с литеральной точкой. Внутри класса символов [.] тоже совпадает с литеральной точкой: большинство метасимволов теряют свою силу внутри [...], кроме ], \, ^ (когда стоит первым) и - (в середине).

3.2 Сокращения для символов

Сокращённые классы выглядят просто ровно до встречи с Unicode:

// JavaScript — без флага u, \d работает только с ASCII
/\d/.test('5');    // true
/\d/.test('٥');    // false (арабо-индийская цифра)
/\d/u.test('٥');   // false — даже с u, \d остаётся ASCII в JS
/\p{N}/u.test('٥'); // true — \p{N} это Unicode-осведомлённый класс цифр
# Python — модуль re по умолчанию трактует \d как Unicode
import re
re.match(r'\d', '٥')  # <Match span=(0, 1)>
re.match(r'(?a)\d', '٥')  # None — (?a) форсирует ASCII

Пока на входе только английский ASCII, \d и [0-9] взаимозаменяемы. Стоит пользователю вставить имя с диакритикой — и вместо \w уже нужен \p{L}.

3.3 Пользовательские классы символов

// JavaScript
/[A-Za-z][A-Za-z0-9_-]{2,29}/.test('valid_handle-1'); // true

// Отрицание и диапазоны вместе
/[^aeiou\s]/g  // любой не-гласный, не-пробельный символ

Для Unicode-категорий \p{L} означает «любая буква», \p{N} — «любое число», \p{Script=Han} — «любой иероглиф Han». JavaScript требует флаг u; Python поддерживает \p{...} только через PyPI-пакет regex, в стандартном re этого нет.

Если вы работаете в командной строке, вам также могут встретиться POSIX-классы символов:

POSIX-классЧто совпадаетASCII-эквивалент
[[:alpha:]]буквы[A-Za-z]
[[:digit:]]цифры[0-9] (\d)
[[:alnum:]]буквы и цифры[A-Za-z0-9]
[[:space:]]пробельные символы\s
[[:upper:]]заглавные буквы[A-Z]
[[:lower:]]строчные буквы[a-z]

POSIX-классы работают в grep -E, sed -E. В JavaScript и в Python re они не работают — используйте \d, \s, \w.

4. Квантификаторы: жадные и ленивые

4.1 Базовые квантификаторы

/a*/.exec('aaab')      // ['aaa']     — 0 или более
/a+/.exec('aaab')      // ['aaa']     — 1 или более
/a?/.exec('aaab')      // ['a']       — 0 или 1
/a{2,3}/.exec('aaaab') // ['aaa']     — от 2 до 3

4.2 Жадные и ленивые

По умолчанию квантификаторы жадные: они захватывают сколько могут, а потом отступают назад, пока весь паттерн не сойдётся. Добавьте ?, и квантификатор станет ленивым.

const html = '<p>one</p><p>two</p>';

html.match(/<p>.*<\/p>/)[0];   // '<p>one</p><p>two</p>'   (жадный съедает оба)
html.match(/<p>.*?<\/p>/)[0];  // '<p>one</p>'             (ленивый останавливается на первом)

Когда извлекаете теги или строки в кавычках, почти всегда нужна именно ленивая версия. Ещё лучше вообще обойтись без . и взять класс с отрицанием: <p>[^<]*</p> работает быстрее, чем <p>.*?</p>, потому что движку некуда возвращаться.

4.3 Катастрофический возврат

Так regex подвешивает сервер. Вложите один квантификатор в другой с пересекающимися совпадениями, и движок прошарит экспоненциальное число путей, прежде чем сдаться.

// Не делайте так
/(a+)+b/.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!'); // выполняется секундами

Для 41 a, за которыми идёт !, движок перебирает порядка 2^41 точек разбиения, прежде чем решит, что b отсутствует. Три способа исправления:

  1. Уплощите паттерн: /a+b/ делает ту же работу без вложенности.
  2. Используйте атомарную группу (Python regex, PCRE, Java, Ruby): (?>a+)+b — после совпадения с a+ движок не возвращается в неё назад.
  3. Смените движок: regexp в Go, RE2 и crate regex для Rust используют NFA с линейным временем и по своей конструкции не способны катастрофически откатываться.

JavaScript и Python re оба откатываются и в стандартной библиотеке атомарных групп не имеют (PyPI-пакет regex для Python их добавляет). Если длина ввода под вашим контролем — это нормально; если ввод приходит от пользователя, сначала проверяйте длину или заранее компилируйте паттерн против RE2.

5. Якоря и границы слов

5.1 ^ и $

По умолчанию ^ соответствует началу всего ввода, а $ — его концу. С флагом m (multiline) они превращаются в начало и конец каждой строки текста:

const log = 'INFO start\nERROR boom\nINFO done';
log.match(/^ERROR.*/);    // null    — однострочный режим, ^ совпадает только с индексом 0
log.match(/^ERROR.*/m);   // ['ERROR boom']

5.2 \b и \B

\b — утверждение нулевой ширины: оно срабатывает на позиции между символом слова (\w) и не-символом слова. Удобно для поиска по целым словам:

/\bcat\b/.test('the cat sat');     // true
/\bcat\b/.test('concatenate');     // false

Границы слов определяются через \w, а он по умолчанию работает только с ASCII. В китайском, японском и корейском тексте между словами нет пробелов, и \b границ слов там не находит. Понадобится токенизатор (jieba, MeCab) — до regex, а не вместо него.

5.3 Многострочный режим

import re
text = "INFO ok\nERROR fail\nINFO done\n"

re.findall(r'^ERROR.*$', text)              # []
re.findall(r'^ERROR.*$', text, re.MULTILINE) # ['ERROR fail']

В JavaScript то же самое пишется как text.match(/^ERROR.*$/gm). Скомбинируйте m и g, чтобы получить каждую совпадающую строку.

6. Группы, захват и обратные ссылки

6.1 Захватывающие группы

Скобки выполняют сразу две роли: группируют подпаттерны для квантификаторов и сохраняют совпадение для дальнейшего использования.

'2026-05-13'.match(/(\d{4})-(\d{2})-(\d{2})/);
// ['2026-05-13', '2026', '05', '13', index: 0, ...]

Группы нумеруются слева направо по их открывающей скобке, начиная с 1.

6.2 Незахватывающие группы

Когда нужна только группировка без захвата — берите (?:...). Так быстрее, и нумерация остальных групп не съезжает:

/(?:https?):\/\/(\S+)/.exec('see https://go-tools.org');
// ['https://go-tools.org', 'go-tools.org']
// — протокол сгруппирован, но не захвачен; группа 1 это хост

6.3 Именованные группы

Имена у групп делают паттерн читаемее и не ломают код при рефакторинге.

// JavaScript (ES2018+)
const m = '2026-05-13'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
m.groups.year;  // '2026'
# Python — обратите внимание на синтаксис (?P<...>)
import re
m = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', '2026-05-13')
m.group('year')  # '2026'

6.4 Обратные ссылки

С обратной ссылкой более поздняя часть паттерна повторяет то, что уже совпало с предыдущим захватом.

// Найти любой символ, который повторяется подряд
'bookkeeper'.match(/(\w)\1/g);   // ['oo', 'kk', 'ee']

// Сопоставить парные HTML-теги по имени
const tag = /<(\w+)>(.*?)<\/\1>/;
'<b>bold</b>'.match(tag);
// ['<b>bold</b>', 'b', 'bold']

В Python \1 работает и в паттерне, и в замене; именованные ссылки в паттерне пишутся как (?P=name), а в заменах re.sub — как \g<name>.

7. Lookaround: lookahead и lookbehind

Lookaround — утверждения нулевой ширины. Они проверяют условие, не потребляя символов, поэтому их можно связывать в цепочки.

7.1 Lookahead

// Пароль: не менее 8 символов, одна цифра, одна заглавная, одна строчная
const strong = /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z]).{8,}$/;
strong.test('Hunter2!');   // true
strong.test('hunter2!');   // false — нет заглавных

// Отрицательный lookahead — имена файлов, которые не .tmp
/^[\w-]+(?!\.tmp$)\.[a-z]+$/.test('report.csv'); // true

7.2 Lookbehind

Lookbehind работает в зеркальную сторону: проверяет то, что стоит перед текущей позицией.

// Извлечь цену после символа валюты — оставить число, отбросить $
'price: $42.50'.match(/(?<=\$)\d+(\.\d+)?/);   // ['42.50', '.50']

// Отрицательный lookbehind — совпасть с Bond, но не с James Bond
'Mr. Bond'.match(/(?<!James )Bond/);  // ['Bond']
'James Bond'.match(/(?<!James )Bond/); // null

7.3 Lookbehind в JavaScript и в Python

Одно из немногих мест, где два движка расходятся достаточно, чтобы при переносе паттерн просто ломался.

ДвижокДлина lookbehind
JavaScript (V8, SpiderMonkey, JSC 16.4+)Переменной ширины с ES2018. (?<=\d+) допустимо.
Python stdlib reТолько фиксированной ширины. (?<=\d+) вызывает error: look-behind requires fixed-width pattern.
PyPI-пакет Python regexПоддерживается переменная ширина. import regex; regex.search(r'(?<=\d+)abc', '12abc').

Обходной путь в Python: переписать lookbehind с известным повтором ((?<=\d{3})) либо захватить префикс и отрезать его уже после совпадения.

8. Флаги и модификаторы

8.1 i — без учёта регистра

/error/i.test('FATAL ERROR'); // true
re.search(r'error', 'FATAL ERROR', re.IGNORECASE)  # <Match span=(6, 11)>

8.2 m и s

Флаг m превращает ^ и $ в построчные якоря. Флаг s (dotall) разрешает . совпадать с переводом строки. Эти два флага независимы, и при необходимости их спокойно ставят вместе.

/<script>(.*?)<\/script>/s.exec('<script>\nalert(1)\n</script>')[1];
// '\nalert(1)\n'  — без s точка отказалась бы от переводов строк

8.3 g — глобальный

В JavaScript флаг g меняет API, а не само совпадение. Без g метод String.match возвращает группы захвата; с g — каждое совпадение целиком, без групп. Чтобы сохранить группы по всем совпадениям, используйте matchAll.

const text = 'a=1 b=2 c=3';

text.match(/(\w)=(\d)/);     // первое совпадение с группами
text.match(/(\w)=(\d)/g);    // ['a=1', 'b=2', 'c=3'] — без групп
[...text.matchAll(/(\w)=(\d)/g)]; // каждое совпадение, с группами

В Python флага g нет: глобальные варианты — это re.findall, re.finditer и re.sub.

8.4 u — Unicode и \p{...}

// Совпасть с любым иероглифом Han (китайский, японский kanji)
/\p{Script=Han}+/gu.test('Hello 世界'); // true

// Совпасть с эмодзи (extended pictographic)
/\p{Extended_Pictographic}/u.test('👋'); // true

В Python Unicode включён по умолчанию; эквивалент для диапазона Han — re.findall(r'[一-鿿]+', text). Полные Unicode-экранирования свойств есть в PyPI-пакете regex: regex.findall(r'\p{Script=Han}+', text).

9. Готовые паттерны на каждый день

9.1 Валидация email

Сначала честно решите, какая версия вам нужна.

// Паттерн на 95% — то, что использует большинство валидаторов форм
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
email.test('a@b.co');  // true

// Паттерн «я действительно хочу быть в духе RFC 5322»
const rfc = /^[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])?)*$/;

По правде говоря, полная валидация email по RFC 5322 на чистом regex занимает около 6000 символов и всё равно спотыкается на краевых случаях. Возьмите паттерн на 95% и отправьте письмо для верификации: это единственный тест, который реально работает.

9.2 Извлечение URL

const urlPattern = /https?:\/\/[^\s<>"]+/g;
const found = 'See https://example.com/a?b=1 and http://x.io'.match(urlPattern);
// ['https://example.com/a?b=1', 'http://x.io']

После того как URL извлечён, обычно хочется заглянуть в query string. Вставьте адрес в наш URL декодер и кодировщик, и percent-encoded параметры станут читаемыми с одного взгляда. Когда кодировать, а когда декодировать, разобрано в руководстве по percent-encoding.

9.3 Номера телефонов

// E.164 — международный, опциональный + и код страны из 1-3 цифр
const e164 = /^\+?[1-9]\d{1,14}$/;
e164.test('+14155551234');  // true

// Североамериканский план нумерации с разделителями
const nanp = /^(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;
nanp.test('(415) 555-1234'); // true

Если нужно что-то серьёзнее проверки «похоже ли это вообще на телефон», берите libphonenumber. Существование кода города regex не проверит.

9.4 IPv4 и IPv6

// IPv4 — строго 0-255 на октет
const ipv4 = /^((25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(25[0-5]|2[0-4]\d|1?\d?\d)$/;
ipv4.test('192.168.1.1');   // true
ipv4.test('999.0.0.1');     // false

// IPv6 — упрощённая форма. Полный паттерн RFC 4291 это ~600 символов.
const ipv6simple = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
ipv6simple.test('2001:0db8:85a3:0000:0000:8a2e:0370:7334'); // true

Для настоящего IPv6 с сокращением ::, встроенным IPv4 и идентификаторами зон возьмите isIP() из node:net или ipaddress.ip_address() в Python. Сделать это на чистом regex — сначала обряд посвящения, потом вечная боль поддержки.

9.5 Даты и timestamp ISO 8601

// Только дата — YYYY-MM-DD
const isoDate = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
isoDate.test('2026-05-13'); // true

// Дата + время + опциональные дробные секунды + Z или смещение
const iso = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
iso.test('2026-05-13T09:30:00.123Z'); // true

ISO 8601 кажется простым, но в нём полно ловушек: секунды координации, недельные даты (2026-W19), порядковые даты (2026-133). Про секунды эпохи и миллисекунды, а также про сдвиги часовых поясов написано в руководстве по Unix timestamp.

10. Рабочие процессы поиска и замены с regex

10.1 JavaScript — String.replace с $1

// Переформатировать даты США: MM/DD/YYYY -> YYYY-MM-DD
'05/13/2026'.replace(/(\d{2})\/(\d{2})\/(\d{4})/, '$3-$1-$2');
// '2026-05-13'

// Использовать callback, когда замена условная
'price 42 dollars'.replace(/(\d+) dollars/, (_, n) => `$${n}`);
// 'price $42'

$1, $2, … обращаются к нумерованным группам, $<name> — к именованным. $& — это полное совпадение, а $$ — литеральный $.

10.2 Python — re.sub с \1 и callback

import re

# То же переформатирование даты, что и выше
re.sub(r'(\d{2})/(\d{2})/(\d{4})', r'\3-\1-\2', '05/13/2026')
# '2026-05-13'

# Callback — привести в верхний регистр каждый email-адрес в строке
def upper_email(m):
    return m.group(0).upper()

re.sub(r'[\w.-]+@[\w.-]+', upper_email, 'mail me at hi@go-tools.org')
# 'mail me at HI@GO-TOOLS.ORG'

В заменах Python пишут \1 или \g<name>. Префикс raw-строки r'...' принципиально важен: без него \1 превратится в литеральный символ.

10.3 CLI: sed, grep, ripgrep, jq

В пакетных рефакторингах в командной строке regex переезжает из скрипта прямо в shell:

# ripgrep — найти каждый TODO с прикреплённым именем
rg -n '\bTODO\(([^)]+)\)' --replace 'TODO(\1)'

# grep -E с якорями — строки неудачных логинов из auth.log
grep -E '^[A-Z][a-z]{2} +[0-9]+ .*Failed password' /var/log/auth.log

# sed — удалить хвостовые пробелы, in-place, по всему дереву
find . -name '*.md' -print0 | xargs -0 sed -i -E 's/[[:space:]]+$//'

ripgrep использует crate regex для Rust (стиль RE2, линейное время, без lookbehind). grep -E и sed -E работают на POSIX extended regex, в котором нет \d: вместо него пишите [0-9] или [[:digit:]]. Когда данные — это JSON, regex стоит сменить на jq: рядом лежит шпаргалка по jq.

11. Распространённые подводные камни

11.1 Забытое экранирование .

Реальная история из продакшена: log redactor должен был маскировать IP-адреса.

// Неправильно — совпадает и с '192a168b1c1'
/(\d+).(\d+).(\d+).(\d+)/.test('192a168b1c1');  // true

// Правильно
/(\d+)\.(\d+)\.(\d+)\.(\d+)/.test('192a168b1c1'); // false

Внутри класса символов . и так литерал, поэтому работают и [.], и \.. Везде в других местах её нужно экранировать.

11.2 Жадный .* съедает слишком много

'<a href="x"><b>bold</b></a>'.match(/<(.*)>/)[1];
// 'a href="x"><b>bold</b></a'  — всё целиком!

Жадный .* доходит до конца строки и затем откатывается назад, пока не упрётся в > — то есть в последний > во вводе. Либо берите ленивую версию (.*?), либо, что быстрее и понятнее, — класс с отрицанием ([^>]*).

11.3 Многострочные якоря

Частое заблуждение: ^ и $ по умолчанию якобы цепляются за переводы строк. Нет, они совпадают с позициями в начале и в конце всего ввода. Флаг m делает их построчными якорями. Флаг s разрешает . пересекать переводы строк. Флаги ортогональны, и для разбора логов обычно нужны оба.

11.4 ReDoS и как его обезвредить

ReDoS — regex denial of service — продакшен-версия катастрофического возврата. Что с этим делать:

  1. Статический анализ. Инструменты вроде safe-regex, recheck и правило ESLint no-misleading-character-class ловят опасные паттерны ещё до релиза.
  2. Атомарные группы (Python regex, PCRE, Ruby, Java): (?>...) не даёт движку повторно зайти в группу при откате.
  3. Притяжательные квантификаторы (*+, ++, ?+ в PCRE/Java): та же идея, синтаксис короче.
  4. Переход на движок без возврата. regexp в Go, RE2, crate regex для Rust и Python-биндинг re2 работают за линейное время. ripgrep — самое массовое применение RE2 в дикой природе.
  5. Сначала валидируйте длину ввода. Regex-бомба на 10 КБ — это баг; ограничение ввода в 10 байт — одна строка кода.

Более широкий взгляд на повседневные инструменты, которые работают в паре с regex (форматтеры, декодеры, конвертеры), есть в руководстве по инструментам разработчика.

Прежде чем отправлять сложный паттерн в продакшен, протестируйте его интерактивно. regex101.com переключается между диалектами PCRE, JavaScript, Python и Go, объясняет каждый токен и показывает возврат, так что катастрофические паттерны видны сразу.

12. FAQ

В чём разница между regex * и +?

* соответствует нулю или более вхождений (то есть совпадает и с пустой строкой); + требует минимум одного вхождения. a* совпадает с '', 'a', 'aaaa'. a+ совпадает с 'a' и 'aaaa', но не с ''.

Как сопоставить regex по нескольким строкам?

Включите multiline-флаг (/.../m в JavaScript, re.MULTILINE в Python), чтобы ^ и $ привязывались к каждой строке. Чтобы . пересекала переводы строк, добавьте dotall-флаг (s в JavaScript, re.DOTALL в Python).

Одинаков ли regex в JavaScript и Python?

Базовый синтаксис (квантификаторы, якоря, классы символов, простые группы) совпадает примерно на 90%. Реальных расхождений два. JavaScript (ES2018+) поддерживает lookbehind переменной длины и пишет именованные группы как (?<name>...). Стандартный re в Python требует lookbehind фиксированной ширины и использует (?P<name>...). Чтобы получить lookbehind переменной длины в Python, поставьте пакет regex из PyPI.

Почему у моего regex катастрофический возврат?

В паттерне сидят вложенные квантификаторы с пересекающимися совпадениями: (a+)+, (a|a)* и тому подобное. На вводе, который почти совпадает, но обламывается ближе к концу, движок перебирает каждое возможное разбиение внутреннего квантификатора — экспоненциально много путей. Лечится атомарной группой (?>a+)+, притяжательным квантификатором a++ или переходом на движок без возврата (RE2, regexp в Go).

Можно ли использовать lookbehind в JavaScript?

Да. Положительный (?<=...) и отрицательный (?<!...) lookbehind есть в V8 (Chrome, Node.js), SpiderMonkey (Firefox) и JavaScriptCore (Safari 16.4+) начиная с ES2018. Lookbehind переменной длины поддерживается. Для более старого Safari транспилируйте через Babel или определяйте поддержку через try/catch вокруг new RegExp.

Как сопоставить литеральную точку . в regex?

Экранируйте её обратным слешем: \. соответствует литеральной точке. Внутри класса символов точка и так литерал, работают и [.], и [\.]. Вне класса неэкранированная . — метасимвол со значением «любой символ, кроме перевода строки» (а с dotall-флагом — вообще любой символ).

Что означает \s в regex?

\s совпадает с любым пробельным символом — пробелом, табуляцией, переводом строки, возвратом каретки. В режиме Unicode он также совпадает с NBSP. \S — обратный класс.

Чувствительны ли регулярные выражения к регистру?

По умолчанию — да. Используйте флаг i в JavaScript (/cat/i) или re.IGNORECASE / (?i) в Python.

Похожие статьи

Все статьи