en.javascript.info/10-regular-expressions-javascript/09-regexp-groups/article.md
Ilya Kantor 62c507c78f ok
2017-03-19 20:40:47 +03:00

7.6 KiB
Raw Blame History

Bracket groups

A part of the pattern can be enclosed in parentheses pattern:(...). That's called a "bracket expression" or a "bracket group".

That has two effects:

  1. It allows to place a part of the match into a separate array item when using String#match or RegExp#exec methods.
  2. If we put a quantifier after the parentheses, it applies to the parentheses as a whole, not the last character.

[cut]

Example

В примере ниже, шаблон pattern:(go)+ находит один или более повторяющихся pattern:'go':

alert( 'Gogogo now!'.match(/(go)+/i) ); // "Gogogo"

Без скобок, шаблон pattern:/go+/ означал бы subject:g, после которого идёт одна или более subject:o, например: match:goooo. А скобки "группируют" pattern:(go) вместе.

Содержимое группы

Скобки нумеруются слева направо. Поисковой движок запоминает содержимое каждой скобки и позволяет обращаться к нему -- в шаблоне и строке замены и, конечно же, в результатах.

Например, найти HTML-тег можно шаблоном pattern:<.*?>.

После поиска мы захотим что-то сделать с результатом. Для удобства заключим содержимое <...> в скобки: pattern:<(.*?)>. Тогда оно будет доступно отдельно.

При поиске методом String#match в результирующем массиве будет сначала всё совпадение, а далее -- скобочные группы. В шаблоне pattern:<(.*?)> скобочная группа только одна:

var str = '<h1>Привет, мир!</h1>';
var reg = /<(.*?)>/;

alert( str.match(reg) ); // массив: <h1>, h1

Заметим, что метод String#match выдаёт скобочные группы только при поиске без флага /.../g. В примере выше он нашёл только первое совпадение match:<h1>, а закрывающий match:</h1> не нашёл, поскольку без флага /.../g ищется только первое совпадение.

Для того, чтобы искать и с флагом /.../g и со скобочными группами, используется метод RegExp#exec:

var str = '<h1>Привет, мир!</h1>';
var reg = /<(.*?)>/g;

var match;

while ((match = reg.exec(str)) !== null) {
  // сначала выведет первое совпадение: <h1>,h1
  // затем выведет второе совпадение: </h1>,/h1
  alert(match);
}

Теперь найдено оба совпадения pattern:<(.*?)>, каждое -- массив из полного совпадения и скобочных групп (одна в данном случае).

Вложенные группы

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

Например, при поиске тега в subject:<span class="my"> нас может интересовать:

  1. Содержимое тега целиком: span class="my".
  2. В отдельную переменную для удобства хотелось бы поместить тег: span.
  3. Также может быть удобно отдельно выделить атрибуты class="my".

Добавим скобки в регулярное выражение:

var str = '<span class="my">';

var reg = /<(([a-z]+)\s*([^>]*))>/;

alert( str.match(reg) ); // <span class="my">, span class="my", span, class="my"

Вот так выглядят скобочные группы:

На нулевом месте -- всегда совпадение полностью, далее -- группы. Нумерация всегда идёт слева направо, по открывающей скобке.

В данном случае получилось, что группа 1 включает в себя содержимое групп 2 и 3. Это совершенно нормальная ситуация, которая возникает, когда нужно выделить что-то отдельное внутри большей группы.

Даже если скобочная группа необязательна и не входит в совпадение, соответствующий элемент массива существует (и равен undefined).

Например, рассмотрим регэксп pattern:a(z)?(c)?. Он ищет "a", за которой не обязательно идёт буква "z", за которой необязательно идёт буква "c".

Если напустить его на строку из одной буквы "a", то результат будет таков:

var match = 'a'.match(/a(z)?(c)?/)

alert( match.length ); // 3
alert( match[0] ); // a
alert( match[1] ); // undefined
alert( match[2] ); // undefined

Массив получился длины 3, но все скобочные группы -- undefined.

А теперь более сложная ситуация, строка subject:ack:

var match = 'ack'.match(/a(z)?(c)?/)

alert( match.length ); // 3
alert( match[0] ); // ac, всё совпадение
alert( match[1] ); // undefined, для (z)? ничего нет
alert( match[2] ); // c

Длина массива результатов по-прежнему 3. Она постоянна. А вот для скобочной группы pattern:(z)? в ней ничего нет, поэтому результат: ["ac", undefined, "c"].

Исключение из запоминания через ?:

Бывает так, что скобки нужны, чтобы квантификатор правильно применился, а вот запоминать их содержимое в массиве не нужно.

Скобочную группу можно исключить из запоминаемых и нумеруемых, добавив в её начало pattern:?:.

Например, мы хотим найти pattern:(go)+, но содержимое скобок (go) в отдельный элемент массива выделять не хотим.

Для этого нужно сразу после открывающей скобки поставить ?:, то есть: pattern:(?:go)+.

Например:

var str = "Gogo John!";
*!*
var reg = /(?:go)+ (\w+)/i;
*/!*

var result = str.match(reg);

alert( result.length ); // 2
alert( result[1] ); // John

В примере выше массив результатов имеет длину 2 и содержит только полное совпадение и результат pattern:(\w+). Это удобно в тех случаях, когда содержимое скобок нас не интересует.