# Жадный и ленивый режимы
Теперь время залезть "под капот" регулярных выражений и посмотреть, как происходит поиск.
Это понимание необходимо для того, чтобы искать что-либо сложнее чем /\d+/
.
[cut]
Для примера рассмотрим задачу, которая часто возникает в типографике -- заменить кавычки вида `"..."` на "кавычки-лапки": `«...»`.
Шаблон, который мы будем использовать для поиска подстрок в кавычках, будет выглядеть так:
```js
//+ run
var reg = /".+"/g;
var str = 'a "witch" and her "broom" is one';
alert( str.match(reg) );
```
Запустите этот пример...
Упс! Он не работает! Вместо того, чтобы найти два совпадения "witch"
и "broom"
, он находит одно: "witch" and her "broom"
.
Это как раз тот случай, когда *жадность* -- причина всех зол.
## Алгоритм поиска
Движок регулярных выражений пытается проверить строку на соответствие шаблону, начиная с самой первой, нулевой позиции. Если не получается, он идёт вперёд и пытается найти с 1й позиции и так далее.
Чтобы сделать происходящее максимально наглядным и понятным, проследим, что именно он делает для паттерна ".+"
.
"
.
Движок регулярных выражений пытается найти её на 0й позиции, но там совсем другой символ, поэтому на 0й позиции соответствия явно нет.
Далее он переходит 1ю, 2ю позицию в исходной строке и, наконец, обнаруживает кавычку на 3й позиции:
'w'
вполне подходит:
.+
. Движок регулярных выражений берёт один символ за другим, до тех пор, пока у него это получается.
В данном случае это означает "до конца строки":
.+
и очень рад по этому поводу.
Следующий символ шаблона -- это кавычка. Её тоже необходимо найти, чтобы соответствие было полным. А тут -- беда, ведь поисковый текст завершился!
Движок регулярных выражений понимает, что, наверное, взял многовато .+
и начинает отступать обратно ("фаза бэктрекинга" -- backtracking на англ.).
Иными словами, он сокращает текущее совпадение на один символ:
'"'
не совпадает с 'e'
..+
ещё раз:
'"'
не совпадает с 'n'
. Опять неудача.'.'
до тех пор, пока остаток паттерна не совпадёт:
'?'
после квантификатора, так что он станет таким: *?
или +?
или даже ??
для '?'
.
Чтобы не возникло путаницы -- важно понимать: обычно `?` сам является квантификатором (ноль или один). Но если он стоит *после другого квантификатора (или даже после себя)*, то обретает другой смысл -- в этом случае он меняет режим его работы на ленивый.
Регэксп /".+?"/g
работает, как задумано -- находит отдельно witch
и broom
:
```js
//+ run
var reg = /".+?"/g;
var str = 'a "witch" and her "broom" is one';
alert( str.match(reg) ); // witch, broom
```
Чтобы в точности понять, что происходим, разберём в деталях, как ищется ".+?"
.
'"'
найдена на 3й позиции:
'.'
:
'"'
:
+?
. Квантификаторы +?
и ??
ведут себя аналогично -- "ленивый" движок увеличивает количество повторений только в том случае, если для остальной части шаблона на данной позиции нет соответствия, в то время как жадный сначала берёт столько повторений, сколько возможно, а потом отступает назад.
**Ленивость распространяется только на тот квантификатор, после которого стоит `?`.**
Прочие квантификаторы остаются жадными.
Например:
```js
//+ run
alert( "123 456".match ( /\d+ \d+?/g) ); // 123 4
```
\d+
пытается найти столько символов, сколько возможно (работает жадно), так что он находит 123
и останавливается, поскольку символ пробела ' '
не подходит под \d
.\d+?
.
Он находит один символ '4'
и пытатся проверить, есть ли совпадение с остатком шаблона (после \d+?
).
Здесь мы ещё раз заметим -- ленивый режим без необходимости ничего не возьмёт.
Так как шаблон закончился, то поиск завершается и 123 4
становится результатом."[^"]+"
:
```js
//+ run
var reg = /"[^"]+"/g;
var str = 'a "witch" and her "broom" is one';
alert( str.match(reg) ); // witch, broom
```
Регэксп "[^"]+"
даст правильные результаты, поскольку ищет кавычку '"'
, за которой идут столько не-кавычек (исключающие квадратные скобки), сколько возможно. Так что вторая кавычка автоматически прекращает повторения [^"]+
и позволяет найти остаток шаблона "
.