W3docs

Обратные ссылки в регулярных выражениях: \n и \k<name>

Узнайте об обратных ссылках в JavaScript: что это такое, как использовать \1, \k<name> в паттерне и $1, $<name> в replace().

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

Типичные задачи для обратных ссылок — обнаружение удвоенного слова (the the), поиск строки, обёрнутой в парные кавычки ("..." или '...', но не "...'), или проверка того, что HTML-подобный тег закрыт тем же именем тега. Ничего из этого невозможно сделать с помощью обычных литеральных паттернов, поскольку текст для сопоставления неизвестен до момента выполнения регулярного выражения.

В этом руководстве рассматриваются нумерованные обратные ссылки (\1, \2, …), именованные обратные ссылки (\k<name>), правила нумерации групп, типичные подводные камни и повторное использование захваченного текста в String.prototype.replace().

Как использовать обратные ссылки

Внутри паттерна обратный слеш, за которым следует число, ссылается на текст, захваченный захватывающей группой. \1 — это то, что совпало с группой 1, \2 — с группой 2, и так далее. Ключевой момент: обратная ссылка совпадает с захваченным текстом, а не повторно применяет паттерн группы.

javascript— editable

Здесь (\w+) захватывает слово в группу 1, \s совпадает с пробелом, а \1 требует того же слова снова. Поэтому hello hello совпадает, а hello world — нет: \1 должна быть равна тому, что захватила группа 1, а не просто снова совпасть с \w+.

Как нумеруются группы

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

javascript— editable

Всё совпадение — это группа 0 (m[0]), поэтому первая захватывающая группа — \1, а не \0. Для вложенных групп внешняя группа получает меньший номер, поскольку её ( встречается первой.

Использование именованных групп

Нумерованные ссылки трудно читать, когда паттерн разрастается. Вместо этого можно назвать группу с помощью (?<name>…) и ссылаться на неё через \k<name>. Подробнее об объявлении именованных групп см. в разделе Захватывающие группы.

javascript— editable

Здесь (?<word>\w+) — это именованная группа, а \k<word> — обратная ссылка на неё. После успешного совпадения захваченный текст также доступен через объект match.groups. Именованные группы и \k<name> работают во всех современных браузерах и актуальных версиях Node.js без каких-либо флагов.

Повторное использование захватов в replace()

Наиболее распространённое повседневное применение обратных ссылок — не внутри паттерна, а в строке замены String.prototype.replace(). Там захваченный текст указывается через $1, $2, … (или $<name> для именованных групп).

Удобный пример: схлопывание случайно удвоенного слова до одного:

javascript— editable

Обратите внимание на различие: \1 (обратный слеш) используется внутри паттерна, а $1 (знак доллара) — в строке замены. Путаница между ними — частый источник ошибок.

Подводный камень: неучаствующие группы

Обратная ссылка на группу, которая не участвовала в совпадении, ведёт себя особым образом. Если группа ни разу не совпала (например, она находилась внутри неиспользованной альтернативы), её захваченное значение равно undefined, и в JavaScript обратная ссылка тогда совпадает с пустой строкой — она успешно срабатывает, ничего не потребляя.

javascript— editable

Это легко упустить из виду: можно ожидать, что \1 завершится неудачей, если группа не совпала, но вместо этого она тихо совпадает с пустой строкой. Тщательно структурируйте альтернации, если вам важно, что группа всегда что-то захватила.

Практический пример: парные кавычки

Практический паттерн, который требует обратной ссылки — поиск строки в кавычках, где закрывающая кавычка должна быть тем же символом, что и открывающая: "..." и '...' допустимы, а "...' — нет.

javascript— editable

Группа (['"]) захватывает тот символ кавычки, которым открылась строка, а \1 требует, чтобы закрытие было этим же символом. Обычное выражение ["'].*?["'] не может это обеспечить — оно спокойно сопоставило бы "...'. В этом и состоит разница между опережающей/ретроспективной проверкой (которая только утверждает) и обратной ссылкой (которая снова сопоставляет захваченный текст).

Заключение

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

  • Нумерованные группы считаются по их открывающей (, начиная с \1; всё совпадение — это группа 0.
  • Используйте \1 / \k<name> внутри паттерна, а $1 / $<name>в replace().
  • Неучаствующая группа делает так, что её обратная ссылка совпадает с пустой строкой — следите за своими альтернациями.

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

Практика

Практика
Какое из следующих утверждений об обратных ссылках в регулярных выражениях JavaScript является верным?
Какое из следующих утверждений об обратных ссылках в регулярных выражениях JavaScript является верным?
Was this page helpful?