Таблица синтаксиса, а не учебник
Регулярные выражения плотны по своей природе. Шаблон вида ^(?:\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, могут заставить движок перебрать экспоненциально много способов разбить входные данные, прежде чем он заключит, что совпадения нет; когда шаблон применяется к пользовательскому вводу, вызов проверки превращается в вектор отказа в обслуживании. Если шаблон содержит квантифицированную группу, внутри которой есть свой квантификатор, перепишите его — обычно один класс символов с одним квантификатором делает ту же работу за линейное время. А когда шаблон разрастается больше одной-двух строк, честный ответ чаще всего в том, что нужен настоящий парсер, а не регулярное выражение.