renovations

This commit is contained in:
Ilya Kantor 2015-03-22 00:36:11 +03:00
parent c108f03596
commit 9122b131d0
12 changed files with 580 additions and 52 deletions

View file

@ -1,4 +1,4 @@
# Группы [todo]
# Скобочные группы
Часть шаблона может быть заключена в скобки <code class="pattern">(...)</code>. Такие выделенные части шаблона называют "скобочными выражениями" или "скобочными группами".
@ -9,6 +9,9 @@
</ol>
[cut]
## Пример
В примере ниже, шаблон <code class="pattern">(go)+</code> находит один или более повторяющихся <code class="pattern">'go'</code>:
```js
@ -16,30 +19,57 @@
alert( 'Gogogo now!'.match(/(go)+/i ); // "Gogogo"
```
Без скобок, шаблон <code class="pattern">/go+/</code> означал бы <code class="subject">g</code>, после которого идёт одна или более <code class="subject">o</code>, например: <code class="match">goooo</code>.
Без скобок, шаблон <code class="pattern">/go+/</code> означал бы <code class="subject">g</code>, после которого идёт одна или более <code class="subject">o</code>, например: <code class="match">goooo</code>. А скобки "группируют" <code class="pattern">(go)</code> вместе.
**Скобки нумеруются слева направо. Поисковой движок запоминает содержимое каждой скобки и позволяет обращаться к нему, в том числе -- в шаблоне и строке замены.**
## Содержимое группы
Например, найти HTML-тег можно шаблоном <code class="pattern">&lt;.*?&gt;</code>. Скорее всего, после поиска мы захотим что-то сделать с результатом, и нас будет интересовать содержимое `<...>`.
Скобки нумеруются слева направо. Поисковой движок запоминает содержимое каждой скобки и позволяет обращаться к нему -- в шаблоне и строке замены и, конечно же, в результатах.
Для удобства заключим его в скобки: <code class="pattern">&lt;(.*?)&gt;</code>. Тогда содержимое скобок можно будет получить отдельно.
Например, найти HTML-тег можно шаблоном <code class="pattern">&lt;.*?&gt;</code>.
Используем метод [String#match](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/match). В результирующем массиве будет сначала всё совпадение, а далее -- скобочные группы, в данном случае -- только одна:
После поиска мы захотим что-то сделать с результатом. Для удобства заключим содержимое `<...>` в скобки: <code class="pattern">&lt;(.*?)&gt;</code>. Тогда оно будет доступно отдельно.
При поиске методом [String#match](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/match) в результирующем массиве будет сначала всё совпадение, а далее -- скобочные группы. В шаблоне <code class="pattern">&lt;(.*?)&gt;</code> скобочная группа только одна:
```js
//+ run
var str = '<h1>Привет, мир!</h1>'
var reg = /<(.*?)>/
var str = '<h1>Привет, мир!</h1>';
var reg = /<(.*?)>/;
alert(str.match(reg)) // массив: <h1>, h1
alert( str.match(reg) ); // массив: <h1>, h1
```
Для поиска всех совпадений, как мы обсуждали ранее, используется метод [RegExp#exec](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec).
Заметим, что метод [String#match](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/match) выдаёт скобочные группы только при поиске без флага `/.../g`. В примере выше он нашёл только первое совпадение <code class="match">&lt;h1&gt;</code>, а закрывающий <code class="match">&lt;/h1&gt;</code> не нашёл, поскольку без флага `/.../g` ищется только первое совпадение.
**Скобки могут быть и вложенными. В этом случае нумерация также идёт слева направо.**
Для того, чтобы искать и с флагом `/.../g` и со скобочными группами, используется метод [RegExp#exec](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec):
Например, в строке <code class="subject">&lt;span class="my"&gt;</code> нас может интересовать отдельно тег `span` и, для примера, его первая буква.
```js
//+ run
var str = '<h1>Привет, мир!</h1>';
var reg = /<(.*?)>/g;
var match;
while ((match = reg.exec(str)) !== null) {
// сначала выведет первое совпадение: <h1>,h1
// затем выведет второе совпадение: </h1>,/h1
alert(match);
}
```
Теперь найдено оба совпадения <code class="pattern">&lt;(.*?)&gt;</code>, каждое -- массив из полного совпадения и скобочных групп (одна в данном случае).
## Вложенные группы
Скобки могут быть и вложенными. В этом случае нумерация также идёт слева направо.
Например, при поиске тега в <code class="subject">&lt;span class="my"&gt;</code> нас может интересовать:
<ol>
<li>Содержимое тега целиком: `span class="my"`.</li>
<li>В отдельную переменную для удобства хотелось бы поместить тег: `span`.</li>
<li>Также может быть удобно отдельно выделить атрибуты `class="my"`.</li>
</ol>
Добавим скобки в регулярное выражение:
@ -47,15 +77,18 @@ alert(str.match(reg)) // массив: <h1>, h1
//+ run
var str = '<span class="my">';
reg = /<(([a-z])[a-z0-9]*).*?>/;
reg = /<(([a-z]+)\s*([^>]*))>/;
alert( str.match(reg) ); // <span class="my">, span, s
```
Вот так выглядят скобочные группы:
<img src="groups.png">
На нулевом месте -- всегда совпадение полностью, далее -- группы. Их вложенность означает всего лишь, что группа 1 содержит группу 2. Нумерация всегда идёт слева направо, по открывающей скобке.
<img src="regexp-nested-groups.svg">
На нулевом месте -- всегда совпадение полностью, далее -- группы. Нумерация всегда идёт слева направо, по открывающей скобке.
В данном случае получилось, что группа 1 включает в себя содержимое групп 2 и 3. Это совершенно нормальная ситуация, которая возникает, когда нужно выделить что-то отдельное внутри большей группы.
**Даже если скобочная группа необязательна и не входит в совпадение, соответствующий элемент массива существует (и равен `undefined`).**
@ -87,40 +120,32 @@ alert( match[1] ); // undefined, для (z)? ничего нет
alert( match[2] ); // c
```
Длина массива результатов по-прежнему `3`. Она постоянна. А вот для скобочной группы <code class="pattern">(z)?</code> в ней ничего нет.
Длина массива результатов по-прежнему `3`. Она постоянна. А вот для скобочной группы <code class="pattern">(z)?</code> в ней ничего нет, поэтому результат: `["ac", undefined, "c"]`.
**Скобочную группу можно исключить из запоминаемых и нумеруемых, добавив в её начало <code class="pattern">?:</code>**
## Исключение из запоминания через ?:
Бывает так, что скобки нужны, чтобы квантификатор правильно применился, а вот запоминать её в массиве не нужно. Тогда мы просто ставим сразу после открывающей скобки `?:`
Бывает так, что скобки нужны, чтобы квантификатор правильно применился, а вот запоминать её в массиве не нужно.
В примере ниже есть скобочная группа <code class="pattern">(go-?)</code>, которая сама по себе не интересна, но входит в результаты:
Скобочную группу можно исключить из запоминаемых и нумеруемых, добавив в её начало <code class="pattern">?:</code>.
Например, мы хотим найти <code class="pattern">(go)+</code>, но содержимое скобок (`go`) в отдельный элемент массива выделять не хотим.
Для этого нужно сразу после открывающей скобки поставить `?:`, то есть: <code class="pattern">(?:go)+</code>.
Например:
```js
//+ run
var str = "Go-go John!";
var str = "Gogo John!";
*!*
var reg = /(go-?)* (\w+)/i;
var reg = /(?:go)+ (\w+)/i;
*/!*
var result = str.match(reg);
alert( result[0] ); // Go-go John
alert( result[1] ); // go
alert( result[2] ); // John
```
Исключим её из запоминаемых:
```js
//+ run
var str = "Go-go John!";
*!*
var reg = /(?:go-?)* (\w+)/i;
*/!*
var result = str.match(reg);
alert( result[0] ); // Go-go John
alert( result.length ); // 2
alert( result[1] ); // John
```
В примере выше массив результатов имеет длину `2` и содержит только полное совпадение и результат <code class="pattern">(\w+)</code>. Это удобно в тех случаях, когда содержимое скобок нас не интересует.