Если регулярка не срабатывает, проверьте сначала эти три вещи

Написать регулярку — десять минут. Понять, почему она не совпадает, часто стоит часа. Здесь зафиксирован порядок проверки.

Регулярка готова, половина тестовых строк совпадает, остальные нет. Дорого всегда не написание, а поиск причины. Три ловушки ниже покрывают большинство случаев, и у каждой есть фиксированный порядок проверки.

Разделите два вида неудачи

Нет совпадения и совпадение с неверным содержимым — разные проблемы. Первое обычно означает неверный класс символов или флаг, второе почти всегда связано с жадными квантификаторами и расположением скобок. Определив тип, вы экономите половину времени: сначала проверьте, есть ли совпадение, затем — что попало в первую группу.

Ловушка 1: жадный квантификатор съедает остаток

.* по умолчанию жадный: он доходит до конца строки и затем откатывается. Поэтому title="(.*)" на строке title="a" id="b" вернёт a" id="b, а не a.

Два решения: сделать квантификатор ленивым (.*?) или использовать точный класс символов ([^"]*). Второй вариант обычно быстрее и стабильнее — ленивые квантификаторы всё равно откатываются, а классы символов нет.

Ловушка 2: лишний (или недостающий) уровень экранирования

Когда регулярка записана в строковом литерале, обратные слэши проходят через два парсера. В JavaScript нужно писать \\d, чтобы получить регулярку \d; если скопировать \\\\d откуда-то, шаблон начнёт искать буквальный обратный слэш и d. То же с \s, \. и \/.

Быстрая проверка: выведите итоговую строку шаблона в отладчике и сравните с ожидаемой. Это снимает большую часть случаев «я же написал, а оно не совпадает».

Ловушка 3: флаги m и g

Без m символы ^ и $ совпадают только с началом и концом всей строки, поэтому построчная проверка в многострочном тексте не работает. С m они учитывают границы каждой строки.

С g объект регулярки запоминает позицию последнего совпадения (lastIndex). Если использовать одну и ту же регулярку с g в цикле, второй вызов продолжит с прошлой позиции — выглядит как пропуск совпадений. Создавайте новый экземпляр на каждую итерацию или сбрасывайте lastIndex в ноль.

Порядок проверки из четырёх шагов

  1. Сократить вход: оставить минимальную строку, на которой ошибка воспроизводится
  2. Разделить шаблон: разбить по | или убрать необязательные части, пока не найдётся сбойный участок
  3. Посмотреть группы: проверить, что реально попало в каждую группу
  4. Проверить другие входы: добавить строку, которая должна совпасть, и строку, которая не должна

Четвёртый шаг пропускают чаще всего. Совпадение с целью не доказывает, что шаблон отвергает остальное — лишние совпадения вредят сильнее, например когда вместо простого текста заменяется целый фрагмент HTML.

Несколько слов о производительности

Вложенные квантификаторы вроде (a+)+$ на длинном несовпадающем вводе вызывают катастрофический откат и могут заморозить браузер на секунды. Проверка простая: удвойте длину входа и посмотрите, выросло ли время больше чем в четыре раза. Если да — не применяйте такой шаблон напрямую к пользовательскому вводу.

Одной фразой

Сначала определите, нет совпадения или неверное содержимое, затем проверяйте жадность, экранирование и флаги именно в этом порядке.

Три числа, которые читают первыми

Открывая тестер регулярок, отвлекитесь от красивой подсветки и посмотрите сначала на три вещи:

  • Общее число совпадений. Ноль означает «не совпало вообще ничего» — проблема в наборе символов или флаге. Больше нуля, но меньше ожидаемого — обычно забыт флаг g либо какая-то часть шаблона съедает всё после себя.
  • Начальная и конечная позиция каждого совпадения. Позиции объясняют больше, чем содержимое: если первое совпадение тянется до конца текста, виноват жадный квантификатор.
  • Что реально попало в группу 1. Взгляд на группу отдельно сразу разделяет «не совпало» и «совпало, но взято не то».

Порядок важен: сначала количество, потом позиции, затем группы. В обратном порядке цветная подсветка уведёт взгляд, и всё будет казаться неправильным.

Три слепые зоны в CJK и Unicode

w — это сокращение для [A-Za-z0-9_], и иероглифов там нет вообще, поэтому w+ по строке «张三» вернёт пусто. Для китайского текста используйте [一-龥] или более широкий [一-鿿].

. не совпадает с переводом строки без флага s. На многострочном тексте .* остановится в конце первой строки. Либо [sS]*, либо включите s.

Границы слова  для китайского почти бесполезны: слова не разделены пробелами, границы просто нет. Сегментация требует отдельной библиотеки, а не регулярки.

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

Закрепите шаблон таблицей случаев

«При проверке проходило» не значит, что регулярка верна. Надёжный подход — три-пять случаев на каждый шаблон, записанных в небольшую таблицу:

  • Должно совпасть: обычный ввод плюс граничные — самый короткий, самый длинный, ровно на границе.
  • Не должно совпасть: пустая строка, только пробелы, грязные данные с лишними префиксами или суффиксами.
  • Склонно к лишним совпадениям: похожие, но иные по классу входы — для телефона номер с префиксом или городской, начинающийся с 1.

Таблица окупается позже: когда через полгода кто-то правит шаблон, прогон случаев сразу покажет, не сломалось ли прежнее поведение. Без таблицы любая правка — правка вслепую.

Где регулярке не место

Регулярное выражение — не универсальный инструмент, в некоторых местах оно обречено:

  • Разбор вложенного HTML или XML. Теги вкладываются произвольно, а рекурсии у регулярок нет. <div>.*?</div> всегда остановится на первом закрывающем теге и ошибётся во внешней структуре. Нужен DOM-парсер.
  • Разбор JSON. В строках допустимы любые экранирования и скобки, регулярка их перепутает. Нужен JSON.parse.
  • Полная проверка адреса почты. RFC 5322 допускает огромное разнообразие форм, а погоня за 100% точностью тяжела в написании и поддержке. Практично: ограничить длину, требовать один @ и хотя бы одну точку, затем отправить письмо с подтверждением.
  • Очень длинный ввод. Шаблон с вложенными квантификаторами даёт экспоненциальный откат на десятках тысяч несовпадающих символов и вешает браузер. Сначала ограничьте длину.

Критерий прост: допускает ли целевая структура произвольную вложенность или рекурсию? Если да — регулярка не подходит.

Связанные инструменты