en.javascript.info/11-regular-expressions-javascript/9-regexp-groups/article.md
2015-03-24 00:03:51 +03:00

8.6 KiB
Raw Blame History

Скобочные группы

Часть шаблона может быть заключена в скобки (...). Такие выделенные части шаблона называют "скобочными выражениями" или "скобочными группами".

У такого выделения есть два эффекта:

  1. Он позволяет выделить часть совпадения в отдельный элемент массива при поиске через [String#match](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/match) или [RegExp#exec](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec).
  2. Если поставить квантификатор после скобки, то он применится *ко всей скобке*, а не всего лишь к одному символу.

[cut]

Пример

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

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

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

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

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

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

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

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

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

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

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

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

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

var match;

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

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

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

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

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

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

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

//+ run
var str = '<span class="my">';

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

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

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

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

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

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

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

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

//+ run
match = 'a'.match(/a(z)?(c)?/)

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

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

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

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

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

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

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

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

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

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

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

Например:

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

var result = str.match(reg);

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

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