Пока мы с вами разобрали только один метод 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, а остальные карманы просто группировка. А если что-то поменяется - то придется все пересчитывать. Тут точно лучше использовать (?: ) для группировки, а ( ) - только для карманов.