Продолжаем знакомство с регулярными выражениями JavaScript.

Фигурные скобки

Операторы '+', '*', '?' не слишком универсальные (хотя и используются очень часто). А если нам нужно указать конкретное число повторений?

На помощь придет оператор {}, указывающий количество повторений.

Работает он следующим образом: {5} – пять повторений, {2,5} – повторяется от двух до пяти (оба включительно), {2,} - повторяется два и более раз. Обратите внимание на то, что такого варианта - {,2} - нет. Посмотрите примеры:

'xx xax xaax xaaax'.replace(/xa{1,2}x/g, '!'); //вернет 'xx ! ! xaaax'

В данном случае шаблон поиска выглядит так: буква 'x', буква 'a' один или два раза, буква 'x'.

'xx xax xaax xaaax'.replace(/xa{2,}x/g, '!'); //вернет 'xx xax ! !'

В данном случае шаблон поиска выглядит так: буква 'x', буква 'a' два раза и более, буква 'x'.

'xx xax xaax xaaax'.replace(/xa{2}x/g, '!'); //вернет 'xx xax ! xaaax'

В данном случае шаблон поиска выглядит так: буква 'x', буква 'a' два раза, буква 'x'.

Данную задачу, конечно же, можно решить и так (здесь это будет даже короче):

'xx xax xaax xaaax'.replace(/xaax/g, '!'); //вернет 'xx xax ! xaaax'

Однако второй вариант не всегда короче - сравните два выражения:

'строка'.replace(/xaaaaaaaaaax/g, '!');
'строка'.replace(/xa{10}x/g, '!'); 

Согласитесь - теперь второе проще.

А вот так работать не будет:

'xax xaax xaaax'.replace(/xa{,2}x/g, '!'); //ожидали '! ! xaaax'

Мы хотели такой шаблон: буква 'x', буква 'a' два раза и меньше, буква 'x', но, к сожалению, такое не работает.

Нужно указать явно:

'xx xax xaax xaaax'.replace(/xa{1,2}x/g, '!'); //вернет '! ! xaaax'

Вот теперь получим то, что хотели.

Обратите внимание на то, что ноль тоже допустим:

'xx xax xaax xaaax'.replace(/xa{0,2}x/g, '!'); //вернет '! ! ! xaaax'

В данном случае шаблон поиска выглядит так: буква 'x', буква 'a' ноль, один или два раза, буква 'x'.

Под шаблон попала и подстрока 'xx' - там буква 'a' стоит ноль раз, то есть ее нет совсем.

Группы символов \s, \S, \w, \W, \d, \D

Существуют специальные команды, которые позволяют выбрать сразу целые группы символов. Например, \d означает 'цифра от 0 до 9', а \D – наоборот, 'не цифра'. Посмотрите примеры:

'1 12 123 abc @@@'.replace(/\d+/g, '!'); //вернет '! ! ! abc @@@'

В данном примере шаблон поиска выглядит так: цифра от 0 до 9 один или более раз.

Обратите внимание на то, что оператор + считает данные команды одним целым (группирующие скобки не нужны). Это относится ко всем операторам повторения.

'123abc3@@'.replace(/\D+/g, '!'); //вернет '123!3!'

А теперь все будет наоборот: все что угодно, но не цифра от 0 до 9, один или более раз.

Что обозначают разные группы символов:

Символ Его значение
\s Обозначает пробел или пробельный символ (имеется ввиду перевод строки, табуляция и т.п.).
\S Не пробел, то есть всё, кроме \s.
\w Цифра или буква (кириллица не входит в буквы).
\W Не цифра и не буква.
\d Цифра от 0 до 9.
\D Не цифра от 0 до 9.

Как вы можете заметить, большая буква означает отрицание: если \s - пробел, то \S – не пробел и так далее. Посмотрите примеры для закрепления:

'1 12 123 abc @@@'.replace(/\s/g, '!'); //вернет '1!12!123!abc!@@@'

В данном примере шаблон поиска выглядит так: пробельный символ один раз.

'1 12 123 abc @@@'.replace(/\S+/g, '!'); //вернет '! ! ! ! !'

В данном примере шаблон поиска выглядит так: НЕ пробельный символ один или более раз. Все подстроки, разделенные пробелами, заменятся на '!'.

'1 12 123a Abc @@@'.replace(/\w+/g, '!'); //вернет '! ! ! ! @@@'

В данном примере шаблон поиска выглядит так: цифра или буква один или более раз. Все подстроки, состоящие из цифр и букв, заменятся на '!'.

'1 12 123 Abc @@@'.replace(/\W+/g, '!'); //вернет '1!12!123!Abc!'

В данном примере шаблон поиска выглядит так: НЕ цифра и НЕ буква один или более раз. Под данное определение в нашем случае попадает '@@@' и все пробелы (они ведь тоже не цифры и не буквы!). Обратите внимание на то, что в конце один '!' - в него преобразовалась строка ' @@@' - с пробелом впереди.

Квадратные скобки '[' и ']'

Группы символов \s, \S, \w, \W, \d, \D не очень гибкие. Даже такая простая задача, как найти все буквы (но не цифры) не может быть решена ими. Давайте посмотрим, что еще могут предложить нам регулярные выражения.

А предлагают они нам квадратные скобки '[' и ']', которые представляют собой операцию 'или':

'axx bxx cxx exx'.replace(/[abc]xx/g, '!'); //вернет '! ! ! exx'

В данном примере шаблон поиска выглядит так: первый символ - это буква 'a', 'b' или 'c', потом две буквы 'x'.

Но этим их возможности далеко не исчерпаны. С помощью шляпки '^' мы можем сделать отрицание:

'axx bxx cxx exx'.replace(/[^abc]xx/g, '!'); //вернет 'axx bxx cxx !'

В данном примере шаблон поиска выглядит так: первый символ - это НЕ буква 'a', 'b' или 'c' (любой символ кроме них), потом две буквы 'x'.

Что можно еще:

  • Можно задавать группы символов: [a-z] задаст маленькие латинские буквы, [A-Z] – большие, [0-9] – цифру от 0 до 9.
  • Посложнее: [a-zA-Z] – большие и маленькие латинские буквы, то же самое плюс цифры - [a-zA-Z0-9], и так далее. Порядок значения не имеет, нет разницы [a-zA-Z] или [A-Za-z].
  • Еще посложнее: [2-5] - цифра от 2-х до 5-ти (обе включительно), [a-c] – буквы от 'a' до 'c' по алфавиту (то есть 'a', 'b', 'c') и так далее.

Особенности:

. Шляпка - это спецсимвол внутри [ ] (снаружи, кстати, тоже). Если вам нужна шляпка как символ - просто поставьте ее не в начале (она спецсимвол только вначале: [^d] – так спецсимвол, а [d^] - так уже нет!).

'axx bxx ^xx dxx'.replace(/[^d]xx/g, '!'); //вернет '! ! ! dxx'

Шаблон поиска такой: первый символ - это все кроме 'd', потом две буквы 'x'.

'axx bxx ^xx dxx'.replace(/[d^]xx/g, '!'); //вернет 'axx bxx ! !'

А теперь так: первый символ - это 'd' или '^', потом две буквы 'x'.

Можно не убирать шляпку с первого места, а просто заэкранировать ее с помощью обратного слеша, и она станет обозначать саму себя:

'axx bxx ^xx dxx'.replace(/[\^d]xx/g, '!'); //вернет 'axx bxx ! !'

Шаблон поиска такой: первый символ - это 'd' или '^', потом две буквы 'x'.

. Дефис - тоже спецсимвол внутри [ ] (а вот снаружи - нет!). Если вам нужен сам дефис как символ - то поставьте его там, где он не будет воспринят как разделитель группы.

Почему это важно: вы можете сделать группу символов, сами не заметив этого. К примеру, вот так - '[:-@]' - вы думаете, что выбираете двоеточие, дефис и собаку @, а на самом деле получается группа символов между : и @. В эту группу входят следующие символы: ":, ;, <, =, >, ?".

Откуда они взялись? Из таблицы ASCII - двоеточие имеет номер меньше, чем собака - и получается группа. То есть все группы получаются по таблице ASCII (при желании этим можно пользоваться).

Как с этим бороться: поставьте символ дефиса там, где он точно не будет воспринят как символ группы, например, в начале или в конце (то есть после [ или перед ]):

'axx Axx -xx @xx'.replace(/[a-zA-Z-]xx/g, '!'); //вернет '! ! ! @xx'

Шаблон поиска такой: первый символ - это маленькие, большие буквы или дефис '-', потом две буквы 'x'.

Можно сделать и так:

'axx 9xx -xx @xx'.replace(/[a-z-0-9]xx/g, '!'); //вернет '! ! ! @xx'

Шаблон поиска такой: первый символ - это маленькие латинские, дефис '-' или цифра от 0 до 9, потом две буквы 'x'.

Это работает, так как символы 'z-0' не могут образовать группу, ведь буква 'z' уже входит в группу 'a-z'. Но лучше так не рисковать (мало ли что), а просто ставить дефис в начале или в конце.

Можно также заэкранировать дефис - тогда он будет обозначать сам себя независимо от позиции. Например, вместо '[:-@]' написать '[:\-@]' - и группы уже не будет, а будут три символа - двоеточие, дефис и собака @.

. Спецсимволы внутри [ ] становятся обычными символами (значит их не надо экранировать обратным слешем!).

. Исключение из предыдущего: [ ] имеют свои спецсимволы - это '^' и '-', кроме того, если вам нужны квадратные скобки как символы внутри [ ] - то их нужно экранировать обратным слешем.

. Еще исключение: группы символов \s, \S, \w, \W, \d, \D (и другие аналогичные) будут обозначать именно группы, то есть по-прежнему будут командами.

'3xx axx Axx'.replace(/[\da-z]xx/g, '!'); //вернет '! ! Axx'

Шаблон поиска такой: первый символ - это цифра от 0 до 9 (\d) или маленькая латинская буква (a-z), потом две буквы 'x'.

. Операторы повторений записываются сразу после [ ] (их не надо брать в круглые скобки):

'.xx ..xx .a.xx bxx'.replace(/[.a]+xx/g, '!'); //вернет '! ! ! bxx'

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

Иногда один и тот же эффект можно получить и группами \s, \S, \w, \W, \d, \D, и скобками []. Когда пользоваться группами, а когда []?

Пользуйтесь тем, что записывается короче и выглядит проще: \w проще, чем [a-zA-Z], а \d – короче, чем [0-9]. Хотя вторые варианты могут быть нагляднее ([0-9] сразу понятно, а \d – еще нужно вспомнить, что это).

Но в случае 'цифры от 2-x до 5-ти' - можно сделать только так: [2-5] или, что длиннее, можно и так: [2345]. Тоже вариант, но уж очень длинно.

Особенности кириллицы

Первое: кириллица не входит в \w, нужно делать так: [а-яА-Я].

Второе: так сделать недостаточно [а-яА-Я] - сюда не войдет буква ё, нужно делать так: [а-яА-ЯЁё].

Если нужны только маленькие буквы - тогда просто [а-яё].

'аяя ёяя 2яя'.replace(/[а-яА-ЯЁё]яя#', '!'); //вернет '! ! 2яя'

Шаблон поиска такой: первый символ - это цифра кириллическая буква, потом две буквы 'я'.

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

Существуют специальные символы, которые обозначают начало '^' или конец строки '$'. Посмотрите примеры:

'aaa aaa aaa'.replace(/^aaa/g, '!'); //вернет '! aaa aaa'

Шаблон поиска такой: заменить 'aaa' на '!' только если оно стоит в начале строки.

'aaa aaa aaa'.replace(/aaa$/g, '!'); //вернет 'aaa aaa !'

Шаблон поиска такой: заменить 'aaa' на '!' только если оно стоит в конце строки.

'aaa'.replace(/^a+$/g, '!'); //вернет '!'

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

Обратите внимание на этот прием: когда в начале регулярки стоит '^', а в конце - '$'. Таким образом мы проверяем всю строку целиком на соответствие регулярке.

'Или' через вертикальную черту |

Квадратные скобки не единственный вариант сделать 'или': существует еще вариант через вертикальную черту '|':

'bbbb'.replace(/a|b+|c/g, '!'); //вернет '!'

Шаблон поиска такой: если вся строка - это 'a', или вся строка - один или более букв 'b', или вся строка - это 'c', то заменить ее на '!'.

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

'axx bxx bbxx exx'.replace(/(a|b+)xx/g, '!'); //вернет '! ! ! exx'

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

Данный вариант 'или' отличается от квадратных скобок - он мощнее. Такие вещи, как в последнем примере (в начале 'a' или 'b' один или более раз) через [ ] не сделать.

Для простых вещей '|' лучше не использовать: сравните (a|b|c) или [abc] – второй вариант короче.

Проблема обратного слеша

Обратный слеш сам по себе является спецсимволом JavaScript. Он нужен в таких случаях: var str = 'д'Артаньян' - я хочу поставить одиночную кавычку внутри строки, которая сама в одиночных кавычках. У меня ничего не выйдет - JavaScript выдаст ошибку.

Чтобы поставить кавычку как символ в нашем случае - я должен экранировать ее обратным слешем: var str = 'д\'Артаньян' – вот так будет работать.

А если я хочу поставить обратный слеш как символ вовнутрь строки JavaScript? Придется экранировать его самого: var str = 'обратный слеш \\ - это он'. В результате здесь он будет обозначать сам себя.

Теперь перейдем к регуляркам: обратный слеш является спецсимволом JavaScript и спецсимволом регулярок. Чаще всего нам не нужно удваивать этот слеш для JavaScript (например, мы пишем \s, а не \\s), однако, если мы хотим поставить обратный слеш как символ - нам придется написать его аж 4 раза (2 раза для JavaScript и два раза для регулярки)!

'\\ \\ \\\\'.replace(/\\\\/g, '!'); //вернет '! ! !!'

Шаблон поиска такой: обратный слеш один раз.

Обратите внимание на '\\ \\ \\\\' - мы удваиваем все слеши для JavaScript, и в реальности строка выглядит так: '\ \ \\', поэтому в ответе будет '! ! !!', а не '!! !! !!!!'.

Обратите внимание: здесь + действует аж на 4 символа перед ним без группировки:

'\\ \\ \\\\'.replace(/\\\\+/g, '!'); //вернет '! ! !'

Шаблон поиска такой: обратный слеш один или более раз.