Квантификаторы +, *, ? и {n}

Давайте возьмём строку вида +7(903)-123-45-67 и найдём все числа в ней. Но теперь нас интересуют не цифры по отдельности, а именно числа: 7, 903, 123, 45, 67.

Число — это последовательность из 1 или более цифр \d. Чтобы отметить количество повторений, нам нужно указать квантификатор.

Количество {n}

Самый простой квантификатор — это число в фигурных скобках: {n}.

Он добавляется к символу (или символьному классу, или набору [...] и т.д.) и указывает, сколько их нам нужно.

У него есть несколько вариантов, давайте посмотрим примеры:

Точное количество: {5}

Шаблон \d{5} обозначает ровно 5 цифр, он эквивалентен \d\d\d\d\d.

Следующий пример находит пятизначное число:

alert( "Мне 12345 лет".match(/\d{5}/) ); //  "12345"

Мы можем добавить \b, чтобы исключить числа длиннее: \b\d{5}\b.

Диапазон: {3,5}, от 3 до 5

Для того, чтобы найти числа c разрядностью от 3 до 5 цифр, мы можем указать границы в фигурных скобках: \d{3,5}

alert( "Мне не 12, а 1234 года".match(/\d{3,5}/) ); // "1234"

Верхнюю границу можно не указывать.

Тогда шаблон \d{3,} найдёт последовательность чисел длиной 3 и более цифр:

alert( "Мне не 12, а 345678 лет".match(/\d{3,}/) ); // "345678"

Давайте вернёмся к строке +7(903)-123-45-67.

Число – это последовательность из одной или более цифр. Поэтому шаблон будет \d{1,}:

let str = "+7(903)-123-45-67";

let numbers = str.match(/\d{1,}/g);

alert(numbers); // 7,903,123,45,67

Короткие обозначения

Для самых востребованных квантификаторов есть сокращённые формы записи:

+

Означает «один или более». То же самое, что и {1,}.

Например, \d+ находит числа:

let str = "+7(903)-123-45-67";

alert( str.match(/\d+/g) ); // 7,903,123,45,67
?

Означает «ноль или один». То же самое, что и {0,1}. По сути, делает символ необязательным.

Например, шаблон ou?r найдёт o после которого, возможно, следует u, а затем r.

Поэтому шаблон colou?r найдёт оба: color и colour:

let str = "Следует писать color или colour?";

alert( str.match(/colou?r/g) ); // color, colour
*

Означает «ноль или более». То же самое, что и {0,}. То есть символ может повторяться много раз или вообще отсутствовать.

Например, шаблон \d0* находит цифру вместе со всеми нулями, которые идут за ней (но могут и не идти):

alert( "100 10 1".match(/\d0*/g) ); // 100, 10, 1

Сравните это с '+' (один или более):

alert( "100 10 1".match(/\d0+/g) ); // 100, 10
// 1 не подходит, т.к 0+ требует как минимум один ноль

Ещё примеры

Квантификаторы используются очень часто. Они служат основными «строительными блоками» сложных регулярных выражений, поэтому давайте рассмотрим ещё примеры.

Регулярное выражение для поиска десятичных дробей (чисел с плавающей точкой): \d+\.\d+

В действии:

alert( "0 1 12.345 7890".match(/\d+\.\d+/g) ); // 12.345
Регулярное выражение для поиска «открывающего HTML-тега без атрибутов». Например, <span> или <p>: /<[a-z]+>/i

В действии:

alert( "<body> ... </body>".match(/<[a-z]+>/gi) ); // <body>

Это регулярное выражение ищет символ '<', за которым идут одна или более букв английского алфавита, а затем '>'.

Регулярное выражение для поиска «открывающего HTML-тега без атрибутов» (улучшенный вариант): /<[a-z][a-z0-9]*>/i

Здесь регулярное выражение расширено: в соответствие со стандартом, в названии HTML-тега цифра может быть на любой позиции, кроме первой, например <h1>.

alert( "<h1>Привет!</h1>".match(/<[a-z][a-z0-9]*>/gi) ); // <h1>
Регулярное выражение для поиска «открывающего или закрывающего HTML-тега без атрибутов»: /<\/?[a-z][a-z0-9]*>/i

В предыдущий шаблон мы добавили необязательный слеш /?. Этот символ понадобилось заэкранировать, чтобы JavaScript не принял его за конец шаблона.

alert( "<h1>Привет!</h1>".match(/<\/?[a-z][a-z0-9]*>/gi) ); // <h1>, </h1>
Чтобы регулярное выражение было точнее, нам часто приходится делать его сложнее

В этих примерах мы видим общее правило: чем точнее регулярное выражение – тем оно длиннее и сложнее.

Например, для HTML-тегов, скорее всего, подошло бы и более простое регулярное выражение: <\w+>.

…Но так как класс \w означает любую английскую букву или цифру, или '_', то для такого регулярного выражения подойдут и не теги, например <_>. То есть оно гораздо проще, чем шаблон <[a-z][a-z0-9]*>, но вместе с тем и менее точное.

Подойдёт ли нам <\w+> или нужно использовать <[a-z][a-z0-9]*>?

В реальной жизни допустимы оба варианта. Ответ на подобные вопросы зависит от того, насколько реально важна точность и насколько потом будет сложно или несложно отфильтровать лишние совпадения.

Задачи

важность: 5

Напишите регулярное выражение, которое ищет многоточие (3 и более точек подряд).

Проверьте его:

let reg = /ваше выражение/g;
alert( "Привет!... Как дела?.....".match(reg) ); // ..., .....

Решение:

let reg = /\.{3,}/g;
alert( "Привет!... Как дела?.....".match(reg) ); // ..., .....

Обратите внимание, что точка – это специальный символ. Мы должны экранировать её, то есть вставлять как \..

Напишите регулярное выражение, которое ищет HTML-цвета в формате #ABCDEF: первым идёт символ #, и потом – 6 шестнадцатеричных символов.

Пример использования:

let reg = /...ваше выражение.../

let str = "color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2 #12345678";

alert( str.match(reg) )  // #121212,#AA00ef

P.S. В рамках этого задания не нужно искать цвета, записанные в иных форматах типа #123 или rgb(1,2,3).

Нам нужно найти символ #, за которым следуют 6 шестнадцатеричных символов.

Шестнадцатеричный символ может быть описан с помощью регулярного выражения как [0-9a-fA-F]. Или же как [0-9a-f], если мы используем модификатор i.

Затем мы можем добавить квантификатор {6}, так как нам нужно 6 таких символов.

В результате наше регулярное выражение получилось таким: /#[a-f0-9]{6}/gi.

let reg = /#[a-f0-9]{6}/gi;

let str = "color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2"

alert( str.match(reg) );  // #121212,#AA00ef

Проблема в том, что находятся также совпадения, принадлежащие более длинным последовательностям символов:

alert( "#12345678".match( /#[a-f0-9]{6}/gi ) ) // #12345678

Чтобы исправить это, мы можем добавить в конец нашего регулярного выражения \b:

// цвет
alert( "#123456".match( /#[a-f0-9]{6}\b/gi ) ); // #123456

// не цвет
alert( "#12345678".match( /#[a-f0-9]{6}\b/gi ) ); // null
Карта учебника

Комментарии

перед тем как писать…
  • Если вам кажется, что в статье что-то не так - вместо комментария напишите на GitHub.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.