Захватывающие группы
Захватывающие группы в регулярных выражениях JavaScript: нумерованные, именованные (?<name>), незахватывающие (?:) группы, использование с match, matchAll и replace.
Захватывающая группа — это часть регулярного выражения, заключённая в скобки (...). Она выполняет две задачи: группирует часть шаблона так, чтобы квантификаторы и чередование применялись ко всему фрагменту целиком, и запоминает совпавший текст, чтобы его можно было прочитать позже.
Именно это превращает регулярное выражение из простой проверки «да/нет» в инструмент извлечения данных. Вместо того чтобы просто знать, что дата совпала, можно отдельно получить год, месяц и день. На этой странице рассматриваются нумерованные группы, именованные группы, незахватывающие группы, а также поведение групп при работе с match, matchAll и replace.
Понимание захватывающих групп
При нахождении совпадения всё совпадение сохраняется по индексу 0 результирующего array, а каждая захватывающая группа записывается по следующим индексам (1, 2, 3, …) в том порядке, в котором открывающие скобки групп встречаются слева направо.
Синтаксис и базовое использование
Простой пример:
match[0] — это всегда вся совпавшая строка. match[1] и match[2] содержат текст, захваченный первой и второй парами скобок. Группы нумеруются по их открывающей (, поэтому даже вложенные группы имеют предсказуемый порядок (см. ниже).
Расширенное использование и приёмы
Вложенные захватывающие группы
Группы могут быть вложенными, что позволяет выполнять более сложное сопоставление с образцом. Нумерация следует каждой открывающей скобке слева направо:
Внешняя группа (группа 1) захватывает foobar, а внутренние группы (2 и 3) захватывают foo и bar по отдельности. Если группа является необязательной и не участвует в совпадении, её слот содержит undefined.
Незахватывающие группы
Иногда нужно сгруппировать части шаблона — чтобы применить квантификатор или чередование — без расхода нумерованного слота. Незахватывающие группы используют синтаксис (?:...):
Здесь foo сгруппирован, но не захвачен, поэтому bar становится группой 1, а не группой 2. Незахватывающие группы сохраняют результирующий array чистым и работают немного быстрее, когда совпавший текст не нужен.
Распространённый случай применения — повторение фрагмента без захвата каждого повторения:
Практические примеры
Разбор дат
Захватывающие группы позволяют разбирать и переформатировать даты из строк:
Этот код извлекает год, месяц и день, затем форматирует дату в другом стиле.
Извлечение информации из URL
Ещё один распространённый вариант использования — извлечение частей URL:
Это регулярное выражение отдельно захватывает протокол, домен, путь и строку запроса.
Советы по эффективному использованию захватывающих групп
- Планируйте свои группы: заранее обдумайте, какие части шаблона нужно захватывать.
- Используйте незахватывающие группы при необходимости: применяйте
(?:...)для группировки без захвата, чтобы упростить результирующий array. - Именованные захватывающие группы: в современном JavaScript (ES2018+) можно использовать именованные захватывающие группы для улучшения читаемости.
- Поведение глобального флага: при использовании флага
gвместе сString.prototype.match()возвращается array всех совпавших строк без захватывающих групп. ИспользуйтеString.prototype.matchAll()для детального извлечения групп с глобальным флагом.
Именованные захватывающие группы
Нумерованные группы ненадёжны: добавьте группу в середину — и все индексы после неё сдвинутся. Именованные захватывающие группы (ES2018+) решают эту проблему, присваивая каждой группе метку вида (?<name>...). Захваченные значения появляются в object groups вместо (или в дополнение к) нумерованным индексам:
Для всего, кроме простейших шаблонов, отдавайте предпочтение именованным захватывающим группам — они делают и само регулярное выражение, и код, который его читает, самодокументируемым.
Использование групп с match, matchAll и replace
Способ чтения захваченных групп зависит от вызываемого метода.
Без флага g — match возвращает группы
Не-глобальный String.prototype.match() возвращает один array совпадения со всеми группами, точно так же, как в примерах выше. Это наиболее распространённый способ извлечения одной записи.
С флагом g — используйте matchAll для групп
При добавлении глобального флага g метод match() возвращает плоский array только полных строк совпадений — группы теряются. Чтобы получить группы для каждого совпадения, используйте String.prototype.matchAll(), который возвращает итератор array совпадений (каждый со своим object groups):
Обращение к группам в replace
В строке замены String.prototype.replace() можно ссылаться на захваченные значения по номеру через $1, $2, … или по имени через $<name>:
Для большего контроля можно передать функцию в replace. Её аргументами будут полное совпадение, затем каждая захваченная группа, а в конце (при использовании имён) — object groups:
Обратные ссылки
После того как группа захватила текст, можно снова сопоставить тот же текст позже в том же шаблоне с помощью обратной ссылки — \\1 для нумерованных групп или \\k<name> для именованных. Это удобно для поиска повторяющихся слов или сопоставления парных кавычек:
Обратные ссылки — отдельная тема; подробности см. в разделе Обратные ссылки в шаблоне.
Связанные темы
- Чередование (ИЛИ)
|— группы необходимы для ограничения области чередования. - Квантификаторы
+ * ? {n}— применение повторения ко всей группе целиком. - Жадные и ленивые квантификаторы — управление тем, сколько символов захватывает группа.
- Обратные ссылки
\1и\k<name>— повторное использование захваченного текста внутри шаблона. - Шаблоны и флаги — флаги
g,iи другие, влияющие на сопоставление.
Заключение
Захватывающие группы в регулярных выражениях JavaScript предоставляют мощный инструмент для работы со сложными строковыми шаблонами. Освоив их, вы сможете выполнять сложные текстовые манипуляции, извлекать значимые данные и легко обрабатывать замены. Будь то разбор дат, обработка URL или переформатирование строк — захватывающие группы обеспечивают гибкость и мощь, необходимые для решения задач продвинутой обработки текста. Изучайте приведённые примеры, практикуйтесь на собственных шаблонах и совершенствуйте свои навыки JavaScript для решения любых задач по манипуляциям со строками.