В JavaScript регулярные выражения реализованы отдельным объектом RegExp и интегрированы в методы строк. Регулярное выражение состоит из паттерна (он же «шаблон») и необязательных флагов.

Синтаксис создания регулярного выражения:

var regexp = new RegExp("шаблон", "флаги");

Как правило, используют более короткую запись (шаблон внутри слешей "/"):

var regexp = /шаблон/; // без флагов
var regexp = /шаблон/gmi; // с флагами gmi

Слеши "/" говорят JavaScript о том, что это регулярное выражение. Они играют здесь ту же роль, что и кавычки для обозначения строк.

Использование

Основа регулярного выражения – паттерн. Это строка, которую можно расширить специальными символами, которые делают поиск намного мощнее. В простейшем случае, если флагов и специальных символов нет, поиск по паттерну – то же самое, что и обычный поиск подстроки:

var str = "Я люблю JavaScript!"; // будем искать в этой строке

var regexp = /лю/;
alert( str.search(regexp) ); // 2

Флаги

Регулярные выражения могут иметь флаги, которые влияют на поиск. В JavaScript их всего три:

  • i - Если этот флаг есть, то регэксп ищет независимо от регистра, то есть не различает между А и а.
  • g - Если этот флаг есть, то регэксп ищет все совпадения, иначе – только первое.
  • m - Многострочный режим.

Самый простой для понимания из этих флагов – безусловно, i. Пример его использования:

var str = "Я люблю JavaScript!"; // будем искать в этой строке

alert( str.search( /ЛЮ/ ) ); // -1
alert( str.search( /ЛЮ/i ) ); // 2
  1. С регом /ЛЮ/ вызов вернул -1, что означает «не найдено»,
  2. С регом /ЛЮ/i вызов нашёл совпадение на позиции 2, так как стоит флаг i, а значит «лю» тоже подходит.

Методы RegExp и String

Регулярные выражения в JavaScript являются объектами класса RegExp. Кроме того, методы для поиска по регулярным выражениям встроены прямо в обычные строки String. К сожалению, общая структура встроенных методов слегка запутана, поэтому мы сначала рассмотрим их по отдельности, а затем – рецепты по решению стандартных задач с ними.

str.search(reg)

Этот метод мы уже видели. Он возвращает позицию первого совпадения или -1, если ничего не найдено.

Ограничение метода search – он всегда ищет только первое совпадение. Нельзя заставить search искать дальше первого совпадения, такой синтаксис попросту не предусмотрен. Но есть другие методы, которые это умеют.

str.match(reg) без флага g

Метод str.match работает по-разному, в зависимости от наличия или отсутствия флага g, поэтому сначала мы разберём вариант, когда его нет. В этом случае str.match(reg) находит только одно, первое совпадение. Результат вызова – это массив, состоящий из этого совпадения, с дополнительными свойствами index – позиция, на которой оно обнаружено и input – строка, в которой был поиск. Например:

var str = "ОЙ-Ой-ой";

var result = str.match( /ой/i );

alert( result[0] ); // ОЙ  (совпадение)
alert( result.index ); // 0 (позиция)
alert( result.input ); // ОЙ-Ой-ой (вся поисковая строка)

str.match(reg) с флагом g

При наличии флага g, вызов match возвращает обычный массив из всех совпадений.

var str = "ОЙ-Ой-ой";

var result = str.match( /ой/ig );

alert( result ); // ОЙ, Ой, ой

Обратите внимание, это важно – если match не нашёл совпадений, он возвращает не пустой массив, а именно null.

str.split(reg|substr, limit)

Разбивает строку в массив по разделителю – регулярному выражению regexp или подстроке substr. Обычно мы используем метод split со строками, вот так:

alert('12-34-56'.split('-')) // [12, 34, 56]

Можно передать в него и регулярное выражение, тогда он разобьёт строку по всем совпадениям. Тот же пример с регэкспом:

alert('12-34-56'.split(/-/)) // [12, 34, 56]

str.replace(reg, str|func)

Швейцарский нож для работы со строками, поиска и замены любого уровня сложности. Его простейшее применение – поиск и замена подстроки в строке, вот так:

// заменить дефис на двоеточие
alert('12-34-56'.replace("-", ":")) // 12:34-56

При вызове со строкой замены replace всегда заменяет только первое совпадение. Чтобы заменить все совпадения, нужно использовать для поиска не строку "-", а регулярное выражение /-/g, причём обязательно с флагом g:

// заменить дефис на двоеточие
alert( '12-34-56'.replace( /-/g, ":" ) )  // 12:34:56

regexp.test(str)

Теперь переходим к методам класса RegExp. Метод test проверяет, есть ли хоть одно совпадение в строке str. Возвращает true/false.

var str = "Люблю регэкспы я, но странною любовью";

// эти две проверки идентичны
alert( /лю/i.test(str) ) // true
alert( str.search(/лю/i) != -1 ) // true

Пример с отрицательным результатом:

var str = "Ой, цветёт калина...";

alert( /javascript/i.test(str) ) // false
alert( str.search(/javascript/i) != -1 ) // false

regexp.exec(str)

Для поиска мы уже видели методы:

search – ищет индекс
match – если регэксп без флага g – ищет совпадение с подрезультатами в скобках
match – если регэксп с флагом g – ищет все совпадения, но без скобочных групп

Метод regexp.exec дополняет их. Он позволяет искать все совпадения.


Классы для поиска типов символов:

  • \d – цифры
  • \D – не-цифры
  • \s – пробельные символы, переводы строки
  • \S – всё, кроме \s
  • \w – латинница, цифры, подчёркивание '_'
  • \W – всё, кроме \w
  • '.' – точка обозначает любой символ, кроме перевода строки

Если хочется поискать именно сочетание "\d" или символ «точка», то его экранируют обратным слэшем, вот так: \. Заметим, что регулярное выражение может также содержать перевод строки \n, табуляцию \t и прочие спецсимволы для строк. Конфликта с классами не происходит, так как для них зарезервированы другие буквы.


Набор

Например, [еао] означает любой символ из этих трёх: 'а', 'е', или 'о'.

Такое обозначение называют набором. Наборы используются в регулярном выражении наравне с обычными символами:

// найти [г или т], а затем "оп"
alert( "Гоп-стоп".match(/[гт]оп/gi) ); // "Гоп", "топ"

Обратим внимание: несмотря на то, что в наборе указано несколько символов, в совпадении должен присутствовать ровно один из них. Поэтому в примере ниже нет результатов:

// найти "В", затем [у или а], затем "ля"
alert( "Вуаля".match(/В[уа]ля/) ); // совпадений нет

Диапазоны

Квадратные скобки могут также содержать диапазоны символов. Например, [a-z] – произвольный символ от a до z, [0-5] – цифра от 0 до 5.

В примере ниже мы будем искать "x", после которого идёт два раза любая цифра или буква от A до F:

// найдёт "xAF"
alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) );

Обратим внимание, в слове Exception есть сочетание xce, но оно не подошло, потому что буквы в нём маленькие, а в диапазоне [0-9A-F] – большие.

Если хочется искать и его тоже, можно добавить в скобки диапазон a-f: [0-9A-Fa-f]. Или же просто указать у всего регулярного выражения флаг i.

Символьные классы – всего лишь более короткие записи для диапазонов, в частности:

\d – то же самое, что [0-9]
\w – то же самое, что [a-zA-Z0-9_]
\s – то же самое, что [\t\n\v\f\r ] плюс несколько юникодных пробельных символов

Диапазоны «кроме»

Кроме обычных, существуют также исключающие диапазоны: [^…].

Квадратные скобки, начинающиеся со знака каретки: [^…] находят любой символ, кроме указанных. Например:

[^аеуо] – любой символ, кроме 'a', 'e', 'y', 'o'
[^0-9] – любой символ, кроме цифры, то же что \D
[^\s] – любой не-пробельный символ, то же что \S

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

Количество повторений символа можно указать с помощью числа в фигурных скобках: {n}. Такое указание называют квантификатором (от англ. quantifier). У него есть несколько подформ записи:

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

Регэксп \d{5} обозначает ровно 5 цифр, в точности как \d\d\d\d\d. Следующий пример находит пятизначное число.

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

Количество от-до: {3,5}

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

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

Последнее значение можно и не указывать. Тогда выражение \d{3,} найдет числа, длиной от трех цифр:

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

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

Для самых часто востребованных квантификаторов есть специальные короткие обозначения.

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

Например, \d+ находит числа – последовательности из 1 или более цифр:

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

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

? Означает «ноль или один», то же что и {0,1}. По сути, делает символ необязательным. Например, регэксп ou?r найдёт o, после которого, возможно, следует u, а затем r. Этот регэксп найдёт or в слове color и our в colour:

var str = "Можно писать color или colour (британский вариант)";

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

* Означает «ноль или более», то же что {0,}. То есть, символ может повторяться много раз или вообще отсутствовать. Пример ниже находит цифру, после которой идёт один или более нулей:

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

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

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

Жадные и ленивые квантификаторы

Квантификаторы – с виду очень простая, но на самом деле очень хитрая штука.

Необходимо очень хорошо понимать, как именно происходит поиск, если конечно мы хотим искать что-либо сложнее чем /\d+/. Для примера рассмотрим задачу, которая часто возникает в типографике – заменить в тексте кавычки вида "..." (их называют «английские кавычки») на «кавычки-ёлочки»: «...». Для этого нужно сначала найти все слова в таких кавычках.

Соответствующее регулярное выражение может выглядеть так: /".+"/g, то есть мы ищем кавычку, после которой один или более произвольный символ, и в конце опять кавычка. Однако, если попробовать применить его на практике, даже на таком простом случае…

var reg = /".+"/g;

var str = 'a "witch" and her "broom" is one';

alert( str.match(reg) ); // "witch" and her "broom"

…Мы увидим, что оно работает совсем не так, как задумано! Вместо того, чтобы найти два совпадения "witch" и "broom", оно находит одно: "witch" and her "broom".

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

То есть, любой символ .+ повторился максимальное количество раз, что и привело к такой длинной строке. А мы, наверное, хотели, чтобы каждая строка в кавычках была независимым совпадением? Для этого можно переключить квантификатор + в «ленивый» режим.

Ленивый режим работы квантификаторов – противоположность жадному, он означает «повторять минимальное количество раз».

Его можно включить, если поставить знак вопроса '?' после квантификатора, так что он станет таким: *? или +? или даже ?? для '?'. Чтобы не возникло путаницы – важно понимать: обычно ? сам является квантификатором (ноль или один). Но если он стоит после другого квантификатора (или даже после себя), то обретает другой смысл – в этом случае он меняет режим его работы на ленивый.

Регэксп /".+?"/g работает, как задумано – находит отдельно witch и broom:

var reg = /".+?"/g;

var str = 'a "witch" and her "broom" is one';

alert( str.match(reg) ); // witch, broom

Ленивость распространяется только на тот квантификатор, после которого стоит ?.


Скобочные группы

Часть шаблона может быть заключена в скобки (...). Такие выделенные части шаблона называют «скобочными выражениями» или «скобочными группами». У такого выделения есть два эффекта:

  • Он позволяет выделить часть совпадения в отдельный элемент массива при поиске через String#match или RegExp#exec.
  • Если поставить квантификатор после скобки, то он применится ко всей скобке, а не всего лишь к одному символу.

В примере ниже, шаблон (go)+ находит один или более повторяющихся 'go':

alert( 'Gogogo now!'.match(/(go)+/i) ); // "Gogogo"

Без скобок, шаблон /go+/ означал бы g, после которого идёт одна или более o, например: goooo. А скобки «группируют» (go) вместе.


Ссылки в строке замены имеют вид $n, где n – это номер скобочной группы. Вместо $n подставляется содержимое соответствующей скобки:

var name = "Александр Пушкин";

name = name.replace(/([а-яё]+) ([а-яё]+)/i, "$2, $1");
alert( name ); // Пушкин, Александр

В примере выше вместо $2 подставляется второе найденное слово, а вместо $1 – первое.


Альтернация (или) |

Альтернация – термин в регулярных выражениях, которому в русском языке соответствует слово «ИЛИ». Она обозначается символом вертикальной черты | и позволяет выбирать между вариантами. Например, нам нужно найти языки программирования: HTML, PHP, Java и JavaScript. Соответствующее регулярное выражение: html|php|java(script)?.

var reg = /html|php|css|java(script)?/gi

var str = "Сначала появился HTML, затем CSS, потом JavaScript"

alert( str.match(reg) ) // 'HTML', 'CSS', 'JavaScript'

Мы уже знаем похожую вещь – квадратные скобки. Они позволяют выбирать между символами, например gr[ae]y найдёт gray, либо grey.

Альтернация работает уже не посимвольно, а на уровне фраз и подвыражений. Регэксп A|B|C обозначает поиск одного из выражений: A, B или C, причём в качестве выражений могут быть другие, сколь угодно сложные регэкспы.

Для указания границ альтернации используют скобки (...), например: before(XXX|YYY)after будет искать beforeXXXafter или beforeYYYafter.


Начало строки ^ и конец $

Знак каретки '^' и доллара '$' имеют в регулярном выражении особый смысл. Их называют «якорями» (anchor – англ.). Каретка ^ совпадает в начале текста, а доллар $ – в конце.

Якоря являются не символами, а проверками. До этого мы говорили о регулярных выражениях, которые ищут один или несколько символов. Если совпадение есть – эти символы включаются в результат. А якоря – не такие. Когда поиск ходит до якоря – он проверяет, есть ли соответствие, если есть – продолжает идти по шаблону, не прибавляя ничего к результату.

Каретку ^ обычно используют, чтобы указать, что регулярное выражение необходимо проверить именно с начала текста. Например, без каретки найдёт все числа:

var str = '100500 попугаев съели 500100 бананов!';
alert( str.match(/\d+/ig) ); // 100500, 500100 (нашло все числа)

А с кареткой – только первое:

var str = '100500 попугаев съели 500100 бананов!';
alert( str.match(/^\d+/ig) ); // 100500 (только в начале строки)

Знак доллара $ используют, чтобы указать, что паттерн должен заканчиваться в конце текста. Аналогичный пример с долларом для поиска числа в конце:

var str = '100500 попугаев съели 500100';
alert( str.match(/\d+$/ig) ); // 500100

Многострочный режим, флаг "m"

Многострочный режим включается, если у регэкспа есть флаг /m. В этом случае изменяется поведение ^ и $. В многострочном режиме якоря означают не только начало/конец текста, но и начало/конец строки.

Начало строки ^

В примере ниже текст состоит из нескольких строк. Паттерн /^\d+/gm берёт число с начала каждой строки:

var str = '1е место: Винни\n' +
  '2е место: Пятачок\n' +
  '33е место: Слонопотам';

alert( str.match(/^\d+/gm) ); // 1, 2, 33

Обратим внимание – без флага /m было бы найдено только первое число:

var str = '1е место: Винни\n' +
  '2е место: Пятачок\n' +
  '33е место: Слонопотам';

alert( str.match(/^\d+/g) ); // 1

Это потому что в обычном режиме каретка ^ – это только начало текста, а в многострочном – начало любой строки. Движок регулярных выражений двигается по тексту, и как только видит начало строки, начинает искать там \d+.

Конец строки $

Символ доллара $ ведёт себя аналогично. Регулярное выражение [а-я]+$ в следующем примере находит последнее слово в каждой строке:

var str = '1е место: Винни\n' +
  '2е место: Пятачок\n' +
  '33е место: Слонопотам';

alert( str.match(/[а-я]+$/gim) ); // Винни,Пятачок,Слонопотам

Без флага m якорь $ обозначал бы конец всего текста, и было бы найдено только последнее слово.


Regex101