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

168 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Квантификаторы +, *, ? и {n}
Рассмотрим ту же задачу, что и ранее -- взять телефон вида `+7(903)-123-45-67` и найти все числа в нём. Но теперь нас интересуют не цифры по отдельности, а именно числа, то есть результат вида `7, 903, 123, 45, 67`.
Для поиска цифр по отдельности нам было достаточно класса `\d`. Но здесь нужно искать *числа* -- последовательности из 1 или более цифр.
## Количество {n}
Количество повторений символа можно указать с помощью числа в фигурных скобках: `{n}`.
Такое указание называют *квантификатором* (от англ. quantifier).
У него есть несколько подформ записи:
<dl>
<dt>Точное количество: `{5}`</dt>
<dd>Регэксп <code class="pattern">\d{5}</code> обозначает ровно 5 цифр, в точности как <code class="pattern">\d\d\d\d\d</code>.
Следующий пример находит пятизначное число.
```js
//+ run
alert( "Мне 12345 лет".match(/\d{5}/) ); // "12345"
```
</dd>
<dt>Количество от-до: `{3,5}`</dt>
<dd>Для того, чтобы найти, например, числа размером от трёх до пяти знаков, нужно указать границы в фигурных скобках: <code class="pattern">\d{3,5}</code>
```js
//+ run
alert( "Мне не 12, а 1234 года".match(/\d{3,5}/) ); // "1234"
```
Последнее значение можно и не указывать. Тогда выражение <code class="pattern">\d{3,}</code> найдет числа, длиной от трех цифр:
```js
//+ run
alert( "Мне не 12, а 345678 лет".match(/\d{3,5}/) ); // "345678"
```
</dd>
</dl>
В случае с телефоном нам нужны числа -- одна или более цифр подряд. Этой задаче соответствует регулярное выражение <code class="pattern">\d{1,}</code>:
```js
//+ run
var str = "+7(903)-123-45-67";
alert( str.match(/\d{1,}/g) ); // 7,903,123,45,67
```
## Короткие обозначения
Для самые часто востребованных квантификаторов есть специальные короткие обозначения.
<dl>
<dt>`+`</dt>
<dd>Означает "один или более", то же что `{1,}`.
Например, <code class="pattern">\d+</code> находит числа -- последовательности из 1 или более цифр:
```js
//+ run
var str = "+7(903)-123-45-67";
alert( str.match(/\d+/g) ); // 7,903,123,45,67
```
</dd>
<dt>`?`</dt>
<dd>Означает "ноль или один", то же что и `{0,1}`. По сути, делает символ необязательным.
Например, регэксп <code class="pattern">ou?r</code> найдёт <code class="match">o</code>, после которого, возможно, следует <code class="match">u</code>, а затем <code class="match">r</code>.
Этот регэксп найдёт <code class="match">or</code> в слове <code class="subject">color</code> и <code class="match">our</code> в <code class="subject">colour</code>:
```js
//+ run
var str = "Можно писать color или colour (британский вариант)";
alert( str.match(/colou?r/g) ); // color, colour
```
</dd>
<dt>`*`</dt>
<dd>Означает "ноль или более", то же что `{0,}`. То есть, символ может повторяться много раз или вообще отсутствовать.
Пример ниже находит цифру, после которой идёт один или более нулей:
```js
//+ run
alert( "100 10 1".match(/\d0*/g) ); // 100, 10, 1
```
Сравните это с `'+'` (один или более):
```js
//+ run
alert( "100 10 1".match(/\d0+/g) ); // 100, 10
```
</dd>
</dl>
## Ещё примеры
Эти квантификаторы принадлежат к числу самых важных "строительных блоков" для сложных регулярных выражений, поэтому мы рассмотрим ещё примеры.
<dl>
<dt>Регэксп "десятичная дробь" (число с точкой внутри): <code class="pattern">\d+\.\d+</code></dt>
<dd>
В действии:
```js
//+ run
alert( "0 1 12.345 7890".match(/\d+\.\d+/g) ); // 123.45
```
</dd>
<dt>Регэксп "открывающий HTML-тег без атрибутов", такой как `<span>` или `<p>`: <code class="pattern">/&lt;[a-z]+&gt;/i</code></dt>
<dd>Пример:
```js
//+ run
alert( "<BODY> ... </BODY>".match(/<[a-z]+>/gi) ); // <BODY>
```
Это регулярное выражение ищет символ <code class="pattern">'&lt;'</code>, за которым идут одна или более букв английского алфавита, и затем <code class="pattern">'&gt;'</code>.
</dd>
<dt>Регэксп "открывающий HTML-тег без атрибутов" (лучше): <code class="pattern">/&lt;[a-z][a-z0-9]*&gt;/i</code></dt>
<dd>
Здесь регулярное выражение расширено: в соответствие со стандартом, HTML-тег может иметь символ на любой позиции, кроме первой, например `<h1>`.
```js
//+ run
alert( "<h1>Привет!</h1>".match(/<[a-z][a-z0-9]*>/gi) ); // <h1>
```
</dd>
<dt>Регэксп "открывающий или закрывающий HTML-тег без атрибутов": <code class="pattern">/&lt;\/?[a-z][a-z0-9]*&gt;/i</code></dt>
<dd>В предыдущий паттерн добавили необязательный слэш <code class="pattern">/?</code> перед тегом. Его понадобилось заэкранировать, чтобы JavaScript не принял его за конец шаблона.
```js
//+ run
alert( "<h1>Привет!</h1>".match(/<\/?[a-z][a-z0-9]*>/gi) ); // <h1>, </h1>
```
</dd>
</dl>
[smart header="Точнее -- значит сложнее"]
В этих примерах мы видим общее правило, которое повторяется из раза в раз: чем точнее регулярное выражение, тем оно длиннее и сложнее.
Например, для HTML-тегов, скорее всего, подошло бы и более короткое регулярное выражение <code class="pattern">&lt;\w+&gt;</code>.
Так как класс `\w` означает "любая цифра или английская буква или `'_'`, то под такой регэксп подойдут и не теги, например <code class="match">&lt;_&gt;</code>. Однако он гораздо проще, чем более точный регэксп <code class="pattern">&lt;[a-z][a-z0-9]*&gt;</code>.
Подойдёт ли нам <code class="pattern">&lt;\w+&gt;</code> или нужно использовать именно <code class="pattern">&lt;[a-z][a-z0-9]*&gt;</code>?
В реальной жизни допустимы оба варианта. Ответ на подобные вопросы зависит от того, насколько реально важна точность и насколько сложно потом будет отфильтровать лишние совпадения (если появятся).
[/smart]