17 июля 2023 г.

Символьные классы

Рассмотрим практическую задачу – у нас есть номер телефона вида "+7(903)-123-45-67", и нам нужно превратить его в строку только из чисел: 79031234567.

Для этого мы можем найти и удалить все, что не является числом. С этим нам помогут символьные классы.

Символьный класс – это специальное обозначение, которое соответствует любому символу из определённого набора.

Для начала давайте рассмотрим класс «цифра». Он обозначается как \d и в регулярном выражении соответствует «любой одной цифре».

Например, давайте найдём первую цифру в номере телефона:

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

let regexp = /\d/;

alert( str.match(regexp) ); // 7

Без флага g регулярное выражение ищет только первое совпадение, то есть первую цифру \d.

Давайте добавим флаг g, чтобы найти все цифры:

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

let regexp = /\d/g;

alert( str.match(regexp) ); // массив совпадений: 7,9,0,3,1,2,3,4,5,6,7

// и можно сделать из них уже чисто цифровой номер телефона
alert( str.match(regexp).join('') ); // 79031234567

Это был символьный класс для цифр. Есть и другие символьные классы.

Наиболее используемые:

\d («d» от английского «digit» означает «цифра»)
Цифра: символ от 0 до 9.
\s («s»: от английского «space» – «пробел»)
Пробельные символы: включает в себя символ пробела, табуляции \t, перевода строки \n и некоторые другие редкие пробельные символы, обозначаемые как \v, \f и \r.
\w («w»: от английского «word» – «слово»)
Символ «слова», а точнее – буква латинского алфавита или цифра или подчёркивание _. Нелатинские буквы не являются частью класса \w, то есть буква русского алфавита не подходит.

Для примера, \d\s\w обозначает «цифру», за которой идёт пробельный символ, а затем символ слова, например 1 a.

Регулярное выражение может содержать как обычные символы, так и символьные классы.

Например, CSS\d соответствует строке CSS с цифрой после неё:

let str = "Есть ли стандарт CSS4?";
let regexp = /CSS\d/

alert( str.match(regexp) ); // CSS4

Также мы можем использовать несколько символьных классов:

alert( "I love HTML5!".match(/\s\w\w\w\w\d/) ); // ' HTML5'

Соответствие (каждому символьному классу соответствует один символ результата):

Обратные символьные классы

Для каждого символьного класса существует «обратный класс», обозначаемый той же буквой, но в верхнем регистре.

«Обратный» означает, что он соответствует всем другим символам, например:

\D
Не цифра: любой символ, кроме \d, например буква.
\S
Не пробел: любой символ, кроме \s, например буква.
\W
Любой символ, кроме \w, то есть не буквы из латиницы, не знак подчёркивания и не цифра. В частности, русские буквы принадлежат этому классу.

Мы уже видели, как сделать чисто цифровой номер из строки вида +7(903)-123-45-67: найти все цифры и соединить их.

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

alert( str.match(/\d/g).join('') ); // 79031234567

Альтернативный, более короткий путь – найти нецифровые символы \D и удалить их из строки:

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

alert( str.replace(/\D/g, "") ); // 79031234567

Точка – это любой символ

Точка . – это специальный символьный класс, который соответствует «любому символу, кроме новой строки».

Для примера:

alert( "Ю".match(/./) ); // Ю

Или в середине регулярного выражения:

let regexp = /CS.4/;

alert( "CSS4".match(regexp) ); // CSS4
alert( "CS-4".match(regexp) ); // CS-4
alert( "CS 4".match(regexp) ); // CS 4 (пробел тоже является символом)

Обратите внимание, что точка означает «любой символ», но не «отсутствие символа». Там должен быть какой-либо символ, чтобы соответствовать условию поиска:

alert( "CS4".match(/CS.4/) ); // null, нет совпадений потому что нет символа для точки

Точка как буквально любой символ, с флагом «s»

Обычно точка не соответствует символу новой строки \n.

То есть, регулярное выражение A.B будет искать символ A и затем B, с любым символом между ними, кроме перевода строки \n:

alert( "A\nB".match(/A.B/) ); // null (нет совпадения)

Но во многих ситуациях точкой мы хотим обозначить действительно «любой символ», включая перевод строки.

Как раз для этого нужен флаг s. Если регулярное выражение имеет его, то точка . соответствует буквально любому символу:

alert( "A\nB".match(/A.B/s) ); // A\nB (совпадение!)
Внимание, пробелы!

Обычно мы уделяем мало внимания пробелам. Для нас строки 1-5 и 1 - 5 практически идентичны.

Но если регулярное выражение не учитывает пробелы, оно может не сработать.

Давайте попробуем найти цифры, разделённые дефисом:

alert( "1 - 5".match(/\d-\d/) ); // null, нет совпадения!

Исправим это, добавив пробелы в регулярное выражение \d - \d:

alert( "1 - 5".match(/\d - \d/) ); // 1 - 5, теперь работает
// или можно использовать класс \s:
alert( "1 - 5".match(/\d\s-\s\d/) ); // 1 - 5, тоже работает

Пробел – это символ. Такой же важный, как любой другой.

Нельзя просто добавить или удалить пробелы из регулярного выражения, и ожидать, что оно будет также работать.

Другими словами, в регулярном выражении все символы имеют значение, даже пробелы.

Итого

Существуют следующие символьные классы:

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

…Но это не всё!

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

Можно искать, в том числе, и по этим свойствам. Для этого нужен флаг u, который мы рассмотрим в следующей главе.

Карта учебника