Шпаргалка по регулярным выражениям

Шпаргалка по регулярным выражениям с поиском для диалекта JavaScript: классы символов, наборы в скобках, квантификаторы, якоря, просмотр по сторонам, группы и флаги — каждый пункт с примером.

Сорок один элемент синтаксиса regex с примерами. Фильтруйте по элементам, описаниям и примерам, затем копируйте нужный синтаксис. Охватывает диалект JavaScript (ECMAScript), используемый в браузерах и Node.js.

Как уверенно читать и писать регулярные выражения

Таблица синтаксиса, а не учебник

Регулярные выражения плотны по своей природе. Шаблон вида ^(?:\d{1,3}\.){3}\d{1,3}$ упаковывает полноценную проверку IPv4 в тридцать символов — это прекрасно, когда вы написали его сами, и мучительно, когда читаете чужой. Основная сложность здесь не в понятиях — просто словарь большой, лаконичный и легко забывается наполовину.

Эта страница — справочная таблица по такому словарю, организованная так, как шаблоны на самом деле строятся: что совпадает с одним символом, как задать собственные наборы символов, как сказать «повтори это», где привязать совпадение, как группировать и перечислять варианты и какие флаги меняют поведение движка. Фильтр ищет одновременно по элементам, описаниям и примерам, поэтому набрав «ленивый», «граница» или «просмотр назад», вы сразу попадёте на нужную строку.

Какому диалекту следуют эти элементы

Регулярные выражения — не один язык. Grep, PCRE, Python, Java, RE2 из Go и JavaScript имеют общее ядро, но расходятся в деталях. Всё перечисленное здесь относится к диалекту JavaScript, определённому в ECMAScript, потому что именно он работает в браузере, в Node.js и в большинстве фронтенд-инструментов.

Стоит отметить три отличия. Во-первых, просмотр назад — (?<=...) и (?<!...) — поддерживается современными движками JavaScript, но отсутствует в RE2 из Go и в старых версиях Safari, поэтому шаблоны, полагающиеся на него, переносимы не везде. Во-вторых, в JavaScript нет флага x для «подробного» режима, поэтому нельзя разнести шаблон на несколько строк с комментариями, как позволяет Python; длинные шаблоны обычно собирают из строковых фрагментов. В-третьих, \d и \w в JavaScript по умолчанию работают только с ASCII, поэтому \w не совпадает с буквами с диакритикой или иероглифами CJK, если не перейти на экранирование свойств Unicode вида \p{L} с флагом u.

Как писать шаблоны, которые останутся поддерживаемыми

Две привычки избавляют от большинства проблем с regex. Первая — привязывать шаблон осознанно. Шаблон без якорей ищет совпадение в любом месте строки — это то, что нужно при сканировании, и почти никогда не то, что нужно при проверке. Добавление ^ и $ превращает «содержит что-то похожее на дату» в «является датой», и на этом различии держится удивительно большая доля ошибок валидации.

Вторая — предпочитать ленивые квантификаторы и отрицающие классы символов жадным маскам. Классическая ошибка — <.+> на тексте, похожем на HTML: поскольку + жадный, он проглатывает всё до последнего > в строке. Запись <.+?> или, что лучше, <[^>]+> совпадает с одним тегом. Вариант с отрицающим классом ещё и заметно быстрее, потому что движку никогда не приходится возвращаться назад.

Наконец, помните о катастрофическом бэктрекинге. Вложенные квантификаторы, как в (a+)+b, могут заставить движок перебрать экспоненциально много способов разбить входные данные, прежде чем он заключит, что совпадения нет; когда шаблон применяется к пользовательскому вводу, вызов проверки превращается в вектор отказа в обслуживании. Если шаблон содержит квантифицированную группу, внутри которой есть свой квантификатор, перепишите его — обычно один класс символов с одним квантификатором делает ту же работу за линейное время. А когда шаблон разрастается больше одной-двух строк, честный ответ чаще всего в том, что нужен настоящий парсер, а не регулярное выражение.

Разработано собственными силами. Таблица синтаксиса описывает диалект регулярных выражений JavaScript (ECMAScript) и работает целиком в вашем браузере.

Частые вопросы

Какой диалект регулярных выражений описывают эти элементы?
Диалект JavaScript (ECMAScript) — тот, что работает в браузерах, Node.js и большинстве фронтенд-инструментов. Основной синтаксис общий с PCRE, Python и Java, поэтому подавляющее большинство этих элементов переносится напрямую, но проверьте просмотр назад и экранирование свойств Unicode, если целитесь в другой движок.
Почему мой шаблон совпадает с большим, чем я ожидал?
Почти всегда потому, что квантификатор жадный. И * и + берут столько, сколько могут, и отдают символы обратно только когда остальная часть шаблона не подходит. Добавьте ?, чтобы сделать их ленивыми, как в .+?, или замените маску отрицающим классом вроде [^>]+, который и точнее, и быстрее.
В чём разница между захватывающей и незахватывающей группой?
(abc) сохраняет совпавший фрагмент, чтобы вы могли обратиться к нему позже через \1 или прочитать из массива результатов. (?:abc) группирует элементы для квантификаторов или альтернативы, ничего не сохраняя. Используйте незахватывающую форму, когда нужна только группировка — так индексы результата остаются стабильными, а накладные расходы немного снижаются.
Почему \w не совпадает с буквами с диакритикой или китайскими символами?
В JavaScript \w определён как [A-Za-z0-9_] и намеренно ограничен ASCII. Чтобы совпадать с буквами любой письменности, используйте экранирование свойств Unicode с флагом u: /\p{L}+/u совпадает с буквами латиницы, кириллицы, греческого письма, ханьцзы, каны и любой другой письменности.
Может ли регулярное выражение быть угрозой безопасности?
Да. Вложенные квантификаторы вида (a+)+b могут вызвать катастрофический бэктрекинг, при котором движок перебирает экспоненциально много вариантов, прежде чем завершиться неудачей. Если такой шаблон применяется к пользовательскому вводу, короткая специально подобранная строка способна подвесить процесс. По возможности переписывайте вложенные квантификаторы в один класс символов.
Стоит ли разбирать HTML или JSON регулярным выражением?
Нет. Оба формата рекурсивны, а регулярные выражения не могут выразить произвольную вложенность. Используйте настоящий парсер: DOMParser для HTML, JSON.parse для JSON. Регулярные выражения хороши для сканирования плоского текста, проверки простых форматов полей и точечного поиска с заменой.