5 июля 2021 г.

Квантификаторы +, *, ? и {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

Для того, чтобы найти числа от 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>.

  1. Самое простое: /<[a-z]+>/i

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

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

  2. Улучшенное: /<[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+>. Но стандарт HTML накладывает более жёсткие ограничения на имя тега, так что более точным будет шаблон <[a-z][a-z0-9]*>.

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

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

Задачи

важность: 5

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

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

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

Решение:

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

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

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

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

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

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

alert( str.match(regexp) )  // #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 regexp = /#[a-f0-9]{6}/gi;

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

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

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

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

Чтобы исправить это, мы можем добавить в конец нашего регулярного выражения \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…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.