Пока мы с вами разобрали только один метод JavaScript для регулярных выражений - это replace. Однако поиском и заменой возможности регулярок далеко не исчерпаны.

Существует еще несколько полезных методов, которые мы и разберем в этом уроке.

Метод test

Рассмотрим метод test, который проверяет, есть ли в строке хотя бы одно совпадение с регуляркой. Если есть - возвращается true, а если нет - false.

Метод работает так: регулярка.test(где искать) - то есть порядок параметров не такой, как в replace.

Смотрите пример:

/a+/.test('eee aaa bbb'); //вернет true

Шаблон поиска такой: буква 'a' один или более раз.

preg_match(/a+/g, 'eee bbb'); //вернет 0

Метод ничего не нашел - вернет false.

Часто данный метод используется для проверки на соответствие регулярному выражению целой строки. Например, мы хотим узнать - данная строка корректный email или нет:

/^[a-zA-Z-.]+@[a-z]+\.[a-z]{2,3}$/.test('my-mail@mail.ru');

Шаблон поиска такой:

[a-zA-Z-.]+ маленькие или большие латинские буквы, точка или '-' 1 или более раз, @ потом @,

[a-z]+ потом маленькие латинские 1 или более раз,

\. потом точка,

[a-z]{2,3} потом маленькие латинские два или три раза (ru, by, com и т.п.).

В результате мы получим true - наша строка будет корректным емэйлом.

А вот так получим false:

/^[a-zA-Z-.]+@[a-z]+\.[a-z]{2,3}$/.test('my-#mail@mail.ru');

Обратите внимание на то, что мы вначале регулярки ставим ^, а в конце $ - этим мы говорим, что под шаблон должна попасть вся строка. Если их не поставить - тогда мы скажем не 'вся строка есть email', а 'в строке есть email':

/[a-zA-Z-.]+@[a-z]+\.[a-z]{2,3}/.test('#$%my-mail@mail.ru&@$'); 

Метод вернет false - он нашел одно совпадение (выделено голубым). Но переданная строка отнюдь не корректный email (она просто содержит его внутри, но по краям - мусор).

Метод match с модификатором g

Давайте теперь разберем следующий полезный метод - метод match. Он позволяет получить ту часть строки, которая попала под регулярное выражение.

Этот метод работает по-разному в зависимости от того, есть модификатор g или нет. Если он есть - метод возвращает массив подстрок, которые попали под регулярное выражение.

Метод работает так: где_искать.match(регулярка).

Давайте рассмотрим его работу на следующем примере:

'a aa aaa aaaa'.match(/a+/g); //вернет ['a', 'aa', 'aaa', 'aaaa']

Шаблон поиска такой: буква 'a' один или более раз.

В случае, если совпадений не было, метод match возвращает null, а не пустой массив. Обратите на это внимание.

Метод match без модификатора g

Если вызвать метод match без модификатора g, то он найдет только первое совпадение с регуляркой.

Однако, вернет он все равно массив - это будет массив, состоящий из найденного совпадения, с дополнительными свойствами: index – позиция, на которой оно обнаружено и input – строка, в которой был поиск (в принципе последнее - бесполезная штука).

А найденная подстрока будет лежать в нулевом элементе возвращенного массива (почему так - поймете дальше).

Как это работает - изучите на следующим примере:

var result = 'aaa bbb ccc'.match(/b+/);

alert(result[0]); //выведет 'bbb' - то, что попало под регулярку
alert(result.index); //выведет 4 - позиция начала 'bbb' в строке
alert(result.input); //выведет 'aaa bbb ccc' - исходную строку

Шаблон поиска такой: буква 'b' один или более раз.

Карманы для метода match без модификатора g

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

Чтобы разобраться с этим непростым понятием - смотрите пример:

var result = 'bbb xaaax ccc'.match(/x(a+)x/);

alert(result[0]); //выведет 'xaaax' - то, что попало под регулярку
alert(result[1]); //выведет 'aaa' - то, что попало в карман

Шаблон поиска такой: буква 'x', затем 'a' один или более раз, затем буква 'x'.

В result[0] попало то, что нашла регулярка, а в result[1] - то, что нашла регулярка в круглых скобках.

То есть карман - это такой способ хранения части того, что мы ищем. Например, мы ищем домены вида domain.ru, но хотим узнать только доменную зону (ru, com и т.п.).

В регулярке придется указать, что нам нужны строки вида 'domain.ru' (иначе мы ничего не найдем - мы не можем просто искать доменные зоны, так как непонятно как их найти, нужно привязаться к доменам), но раз нас интересует только зона - то положим ее в карман. Давайте посмотрим на примере:

var result = 'domain.ru'.match(/[a-z]+\.([a-z]{2,3})/);

Шаблон поиска такой: [a-z]+ - маленькие буквы один или более раз (domain, site и т.п.) \. - точка ([a-z]{2,3}) - маленькие буквы 2 или 3 раза (ru, com, by, net и т.п.)

Посмотрим содержимое result: в result[0] попало то, что нашла регулярка, то есть 'domain.ru', а в result[1] - содержимое кармана, то есть 'ru'.

Можно использовать не один карман, а несколько. Положим в первый карман имя домена, а во второй - зону:

var result = 'domain.ru'.match(/([a-z]+)\.([a-z]{2,3})/);

alert(result[0]); //выведет 'domain.ru' - то, что попало под регулярку
alert(result[1]); //выведет 'domain' - то, что попало в первый карман
alert(result[2]); //выведет 'ru' - то, что попало во второй карман

Карманы нумеруются по порядку в регулярке: /([a-z]+)\.([a-z]{2,3})/ - первые круглые скобки - первый карман, вторые - второй карман и так далее.

Карманы внутри replace

Карманы можно использовать и при работе с методом replace - то, что мы положим в карман, затем может быть использовано во втором параметре: где_заменить.replace(регулярка с карманом, на что заменить).

Если мы что-то положим в карман в регулярке, то в параметре 'на что заменить' мы можем обратиться к этому карману так: $1 – первый карман, $2 второй карман и так далее.

Давайте решим следующую задачу: даны строки вида 'aaa@bbb' - буквы, потом собака, потом буквы. Нужно поменять местами буквы до @ и после. В нашем случае из 'aaa@bbb' сделать 'bbb@aaa':

'a@b aa@bb'.replace(/([a-z]+)@([a-z]+)/g, '$2@$1'); //b@a bb@aa

Шаблон поиска такой: маленькие буквы один или более раз (первый карман), собака, и опять маленькие буквы один или более раз (второй карман).

Шаблон замены такой: мы говорим: замени найденное на $2@$1, где $2 – содержимое второго кармана, а $1 - первого.

Давайте разберем подстроку 'a@b', что с ней происходит: 'a' ложится в первый карман и доступно как $1, 'b' ложится во второй карман и доступно как $2.

При замене $2@$1 мы говорим: $2 (вставится 'b'), собака @, $1 (вставится 'a').

Карман $0 соответствует всему выражению. Давайте заменим подстроки из букв на них самих с '!' по краям:

'aaa bbb'.replace(/[a-z]+/g, '!$0!'); //вернет '!aaa! !bbb!'

!$0! - $0 это найденная строка (сначала 'aaa', потом 'bbb'). Мы говорим: замени 'aaa' на ее саму ($0), но с '!' по краям !$0!. И получаем '!aaa!'. Для 'bbb' аналогично.

Карманы по умолчанию внутри replace

Вы уже знаете, что по умолчанию всегда есть карман $0, в котором лежит попавшая под регулярку подстрока. Однако, в строке замены по умолчанию также доступно еще несколько команд:

  • $` - часть строки до совпадения.
  • $' - часть строки после совпадения.
  • $& - всё найденное совпадение (равно $0).
  • $$ - знак доллара.

Несохраняющие скобки

К сожалению, скобки ( ) выполняют две функции - группировка символов и функцию кармана. А что делать, если нам нужно сгруппировать, но в карман не ложить? Для этого придуманы специальные несохраняющие скобки (выделены красным) (?:a+) - они группируют, но не ложат в карман:

'ababx abe'.replace(/(?:ab)+([a-z])/g, '!$1!'); //вернет '!x! !e!'

Шаблон поиска следующий: 'ab' один или более раз, затем одна буква, которую ложим в карман.

Шаблон замены: заменим найденное на первый карман, обернутый '!' справа и слева.

Так как первый карман - это ([a-z]), то в него попадет сначала 'x', а потом 'e'.

А вот если мы вместо несохраняющих скобок возьмем обычные, то первый карман будет (ab):

'ababx abe'.replace(/(ab)+([a-z])/g, '!$1!'); //вернет '!ab! !ab!'

Обратите внимание: + не размножает карманы - 'abab' превратится в '!', а не '!!'.

Но мы-то хотели, чтобы в карман попадало не 'ab', а то, что после него. Это будет уже второй карман:

'ababx abe'.replace(/(ab)+([a-z])/g, '!$2!'); //вернет '!x! !e!'

Получается немного неудобно - $1 (первый карман) нигде не используется. А начинаем сразу с $2. Чтобы не было таких неудобств - и придуманы скобки (?: ).

Хотя, если вас это не смущает, - можно их и не использовать.

Исключение: когда у вас много группировок, а карманов мало - будет неудобно считать все скобки, чтобы понять, что вам нужно $5 и $9, а остальные карманы просто группировка. А если что-то поменяется - то придется все пересчитывать. Тут точно лучше использовать (?: ) для группировки, а ( ) - только для карманов.