Проверяйте регулярные выражения JavaScript на своём тексте. Переключайте флаги g, i, m, s и u, изучайте каждое совпадение с его позицией и группами захвата, а также смотрите результат замены в реальном времени.
Смотрите каждое совпадение, каждую группу и каждую позицию по мере ввода. Введите шаблон и текст для сопоставления. Каждый результат выводится с позицией и группами захвата, для некорректных шаблонов показывается сообщение об ошибке от самого движка, а поле замены показывает точно то, что вернул бы вызов replace.
//g
Совпадения–
Как читать регулярное выражение так, как это делает движок
Флаги меняют смысл, а не только поиск
Пять флагов — это не настройки удобства, они переопределяют шаблон. Без g поиск возвращает первое совпадение и останавливается; с ним движок хранит курсор lastIndex и проходит всю строку — именно поэтому глобальное регулярное выражение, переиспользуемое между вызовами, может как будто пропускать совпадения. Флаг i приводит регистр по правилам Unicode, а не простым сдвигом ASCII.
Чаще всего неправильно понимают m и s. m меняет только смысл ^ и $, привязывая их к границам строк, а не всего текста; на . он никак не влияет. Наоборот, s меняет только ., позволяя ему совпадать с переводом строки. Название «dotall» полезно запомнить именно поэтому. Флаг u заставляет шаблон работать с кодовыми точками, а не с единицами UTF-16, поэтому . совпадает с эмодзи как с одним символом, а экранирование \u{1F600} становится допустимым. Он же превращает ранее допустимые escape-последовательности в ошибку — вот почему существующий шаблон может сломаться, как только вы его добавите.
Группы, позиции и замена
Каждый участок в скобках по умолчанию захватывается и нумеруется по порядку открывающей скобки, и именно эти номера имеют в виду $1 и $2 в строке замены. Вложенность подчиняется тому же правилу, поэтому в ((a)b) первая группа — это ab, а вторая — a. Когда скобки нужны только для перечисления вариантов или повторения, (?:...) избавляет от лишнего захвата и сохраняет нумерацию понятной.
Позиция, указанная для каждого совпадения, — это смещение первого совпавшего символа, и это важнее, чем кажется. Она позволяет различать два одинаковых совпадения в разных местах, убедиться, что якорь сработал как ожидалось, и распознать классическое совпадение нулевой длины. Шаблон вроде \d* совпадает с пустой строкой в любом месте, поэтому глобальный проход находит совпадение на каждой позиции; движок продвигается вперёд только потому, что реализации принудительно сдвигают курсор, когда совпадение пустое.
Возвраты и ограничение числа совпадений
Большинство движков, включая JavaScript, используют бэктрекинг. Когда часть шаблона не подходит, движок возвращается к последней точке выбора и пробует другой путь. Обычно это незаметно. Катастрофой это становится, когда вложенные квантификаторы приводят к экспоненциальному росту числа путей; классический пример — (a+)+b на длинной последовательности символов a без b. Такой ввод может подвесить вкладку браузера, а если шаблон приходит от пользователя, это превращается в вектор отказа в обслуживании.
Две привычки избавляют почти от всех проблем: используйте конкретные классы символов вместо .*, чтобы варианты не перекрывались, и по возможности привязывайте шаблон якорями, поскольку привязанная неудача отбрасывается сразу, а не повторяется на каждом смещении. Кроме того, этот инструмент ограничивает вывод 500 совпадениями и сообщает об усечении, поэтому «убежавший» шаблон приводит к медленному результату, а не к зависшей странице.
Частые вопросы
Какой диалект регулярных выражений используется?
JavaScript, то есть движок ECMAScript, встроенный в ваш браузер. Шаблоны Perl, PCRE и Python близки, но отличаются в поддержке lookbehind, именованных групп и некоторых escape-последовательностей.
Что на самом деле меняет флаг g?
Без него вы получаете только первое совпадение. С ним движок просматривает всю строку и возвращает все совпадения — именно ими заполняется список здесь.
Почему у меня получаются пустые совпадения?
Ваш шаблон может совпадать с нулём символов, как это делает `\d*`. Тогда глобальный поиск сообщает о совпадении на каждой позиции, а движок вручную сдвигается вперёд, чтобы не зацикливаться.
Как ссылаться на группы захвата в строке замены?
Используйте $1 для первой группы, $2 для второй и $& для всего совпадения. Напишите $$, если нужен буквальный символ доллара.
Что означает «показаны первые 500»?
Список результатов ограничен 500 совпадениями, чтобы слишком широкий шаблон не подвесил страницу. Предпросмотр замены при этом применяется ко всему тексту.
Почему из-за моего шаблона страница работает медленно?
Вложенные квантификаторы вида (a+)+ вызывают экспоненциальный бэктрекинг. Используйте конкретные классы символов вместо .* и привязывайте шаблон якорями, чтобы сопоставление оставалось линейным.