Рассмотрим практическую задачу -- есть телефонный номер "+7(903)-123-45-67"
, и нам нужно найти в этой строке цифры. А остальные символы нас не интересуют.
Для поиска символов определённого вида в регулярных выражениях предусмотрены "классы символов".
Класс символов -- это специальное обозначение, под которое подходит любой символ из определённого набора.
Например, есть класс "любая цифра". Он обозначается \d
. Это обозначение вставляется в шаблон, и при поиске под него подходит любая цифра.
То есть, регулярное выражение pattern:/\d/
ищет ровно одну цифру:
var str = "+7(903)-123-45-67";
var reg = /\d/;
// не глобальный регэксп, поэтому ищет только первую цифру
alert( str.match(reg) ); // 7
...Ну а для поиска всех цифр достаточно добавить к регэкспу флаг g
:
var str = "+7(903)-123-45-67";
var reg = /\d/g;
alert( str.match(reg) ); // массив всех совпадений: 7,9,0,3,1,2,3,4,5,6,7
Это был класс для цифр.
Конечно же, есть и другие.
Наиболее часто используются:
\d
(от английского "digit" -- "цифра")
: Цифра, символ от 0
до 9
.
\s
(от английского "space" -- "пробел")
: Пробельный символ, включая табы, переводы строки и т.п.
\w
(от английского "word" -- "слово")
: Символ "слова", а точнее -- буква латинского алфавита или цифра или подчёркивание '_'
. Не-английские буквы не являются \w
, то есть русская буква не подходит.
Например, pattern:\d\s\w
обозначает цифру, за которой идёт пробельный символ, а затем символ слова.
Регулярное выражение может содержать одновременно и обычные символы и классы.
Например, pattern:CSS\d
найдёт строку match:CSS
, с любой цифрой после неё:
var str = "Стандарт CSS4 - это здорово";
var reg = /CSS\d/
alert( str.match(reg) ); // CSS4
И много классов подряд:
alert( "Я люблю HTML5!".match(/\s\w\w\w\w\d/) ); // 'HTML5'
Совпадение (каждому классу в регэкспе соответствует один символ результата):
Граница слова pattern:\b
-- это особый класс.
Он интересен тем, что обозначает не символ, а границу между символами.
Например, pattern:\bJava\b
найдёт слово match:Java
в строке subject:Hello, Java!
, но не в строке subject:Hello, Javascript!
.
alert( "Hello, Java!".match(/\bJava\b/) ); // Java
alert( "Hello, Javascript!".match(/\bJava\b/) ); // null
Граница имеет "нулевую ширину" в том смысле, что обычно символам регулярного выражения соответствуют символы строки, но не в этом случае.
Граница -- это проверка.
При поиске движок регулярных выражений идёт по шаблону и одновременно по строке, пытаясь построить соответствие. Когда он видит pattern:\b
, то проверяет, что текущая позиция в строке подходит под одно из условий:
- Начало текста, если первый символ
\w
. - Конец текста, если последний символ
\w
. - Внутри текста, если с одной стороны
\w
, а с другой -- не\w
.
Например, в строке subject:Hello, Java!
под \b
подходят следующие позиции:
Как правило, \b
используется, чтобы искать отдельно стоящее слово. Не на русском конечно, хотя подобную проверку, как мы увидим далее, можно легко сделать для любого языка. А вот на английском, как в примере выше или для чисел, которые являются частным случаем \w
-- легко.
Например, регэксп pattern:\b\d\d\b
ищет отдельно двузначные числа. Иными словами, он требует, чтобы до и после pattern:\d\d
был символ, отличный от \w
(или начало/конец текста).
Для каждого класса существует "обратный ему", представленный такой же, но заглавной буквой.
"Обратный" -- означает, что ему соответствуют все остальные символы, например:
\D
: Не-цифра, то есть любой символ кроме \d
, например буква.
\S
: Не-пробел, то есть любой символ кроме \s
, например буква.
\W
: Любой символ, кроме \w
, то есть не латинница, не подчёркивание, не цифра. В частности, русские буквы принадлежат этому классу.
\B
: Проверка, обратная \b
.
В начале этой главы мы видели, как получить из телефона subject:+7(903)-123-45-67
все цифры.
Первый способ -- найти все цифры через match(/\d/g)
.
Обратные классы помогут реализовать альтернативный -- найти все НЕцифры и удалить их из строки:
var str = "+7(903)-123-45-67";
alert( str.replace(/\D/g, "") ); // 79031234567
Заметим, что в регулярных выражениях пробел -- такой же символ, как и другие.
Обычно мы не обращаем внимание на пробелы. Для нашего взгляда строки subject:1-5
и subject:1 - 5
почти идентичны.
Однако, если регэксп не учитывает пробелов, то он не сработает.
Попытаемся найти цифры, разделённые дефисом:
alert( "1 - 5".match(/\d-\d/) ); // null, нет совпадений!
Поправим это, добавив в регэксп пробелы:
alert( "1 - 5".match(/\d - \d/) ); // работает, пробелы вокруг дефиса
Конечно же, пробелы в регэкспе нужны лишь тогда, когда мы их ищем. Лишние пробелы (как и любые лишние символы) могут навредить:
alert( "1-5".match(/\d - \d/) ); // null, так как в строке 1-5 нет пробелов
Короче говоря, в регулярном выражении все символы имеют значение. Даже (и тем более) -- пробелы.
Особым классом символов является точка "."
.
В регулярном выражении, точка pattern:"."
обозначает любой символ, кроме перевода строки:
alert( "Z".match(/./) ); // найдено Z
Посередине регулярного выражения:
var re = /CS.4/;
alert( "CSS4".match(re) ); // найдено "CSS4"
alert( "CS-4".match(re) ); // найдено "CS-4"
alert( "CS 4".match(re) ); // найдено "CS 4" (пробел тоже символ)
Обратим внимание -- точка означает именно "произвольный символ".
То есть какой-то символ на этом месте в строке должен быть:
alert( "CS4".match(/CS.4/) ); // нет совпадений, так как для точки нет символа
В регулярных выражениях есть и другие символы, имеющие особый смысл.
Они используются, чтобы расширить возможности поиска.
Вот их полный список: pattern:[ \ ^ $ . | ? * + ( )
.
Не пытайтесь запомнить его -- когда мы разберёмся с каждым из них по отдельности, он запомнится сам собой.
Чтобы использовать специальный символ в качестве обычного, он должен быть экранирован.
Или, другими словами, перед символом должен быть обратный слэш '\'
.
Например, нам нужно найти точку pattern:'.'
. В регулярном выражении она означает "любой символ, кроме новой строки", поэтому чтобы найти именно сам символ "точка" -- её нужно экранировать: pattern:\.
.
alert( "Глава 5.1".match(/\d\.\d/) ); // 5.1
Круглые скобки также являются специальными символами, так что для поиска именно скобки нужно использовать \(
. Пример ниже ищет строку "g()"
:
alert( "function g()".match(/g\(\)/) ); // "g()"
Сам символ слэш '/'
, хотя и не является специальными символом в регулярных выражениях, но открывает-закрывает регэксп в синтаксисе pattern:/...pattern.../
, поэтому его тоже нужно экранировать.
Так выглядит поиск слэша '/'
:
alert( "/".match(/\//) ); // '/'
Ну и, наконец, если нам нужно найти сам обратный слэш \
, то его нужно просто задублировать.
Так выглядит поиск обратного слэша "\"
:
alert( "1\2".match(/\\/) ); // '\'
Мы рассмотрели классы для поиска типов символов:
\d
-- цифры.\D
-- не-цифры.\s
-- пробельные символы, переводы строки.\S
-- всё, кроме\s
.\w
-- латинница, цифры, подчёркивание'_'
.\W
-- всё, кроме\w
.'.'
-- точка обозначает любой символ, кроме перевода строки.
Если хочется поискать именно сочетание "\d"
или символ "точка", то его экранируют обратным слэшем, вот так: pattern:\.
Заметим, что регулярное выражение может также содержать перевод строки \n
, табуляцию \t
и прочие спецсимволы для строк. Конфликта с классами не происходит, так как для них зарезервированы другие буквы.