Написать пост

Регулярные выражения в JavaScript: это не так страшно, как вы думаете

Логотип компании МТС

Подробный гайд с примерами по регулярным выражениям в JS, позволяющим выполнять любые манипуляции со строками.

Обложка поста Регулярные выражения в JavaScript: это не так страшно, как вы думаете

Регулярные выражения — тема, которой боятся многие разработчики. Между тем они требуются в большинстве вакансий уровня middle. В статье разберём основные символы и способы создания регулярных выражений в JavaScript, которые с небольшими отличиями работают и в других языках программирования.

Рассмотрим тему регулярных выражений от простого к сложному.

Что такое регулярные выражения

Регулярные выражения (ещё их называют Regular Expressions, сокращённо regex или regexp, регулярки) — специальные шаблоны, которые используют для поиска и обработки текста. Для поиска в них можно задавать дополнительные команды, например игнорирование регистра.

В отличие от обычных функций поиска и замены, которые встречаются во всех текстовых редакторах, регулярные выражения могут сочетаться с другим кодом. С их помощью можно выполнять сложные операции с текстом, менять порядок строк, извлекать отдельные слова, валидировать и передавать информацию на сервер.

А ещё регулярки экономят очень много кода. Но есть один недостаток — они часто выглядят пугающе, запутанно и странно, особенно если шаблон сложный. 

Например, этот шаблон ищет email-адреса:

/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/

Почти во всех языках программирования есть регулярные выражения — реализация может отличаться, но основные моменты похожи. Их можно сравнить с отдельным мини-языком, который функционирует сам по себе.

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

Например, нам нужно проверить номер телефона в форме регистрации:

			function validatePhoneNumber(phoneNumber) {

  const phoneRegex = /^(\+7|8)?(\d{10})$/;

  return phoneRegex.test(phoneNumber);

}

// пример использования функции

 let phoneNumber = "+79123456789";

if (validatePhoneNumber(phoneNumber)) {

  console.log("Номер телефона валиден!");

} else {

  console.log("Номер телефона невалиден!");

}
		

В этом примере мы создали функцию validatePhoneNumber, которая принимает номер телефона в качестве аргумента и возвращает true, если номер телефона является действительным номером для России, и false в противном случае.

В регулярном выражении phoneRegex мы использовали символ ^, чтобы указать, что номер телефона должен начинаться с «+7» или «8» (возможно, без плюса). Затем мы использовали символы \d для указания цифр и {10} для указания количества цифр в номере телефона (10 цифр).

В примере использования мы передали строку «+79123456789» функции validatePhoneNumber и проверили результат. 

Как создать регулярное выражение в JavaScript

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

1. Конструктор RegExp()

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

const regex = new RegExp("pattern", "flags");

Где «pattern» — это строка, содержащая регулярное выражение, а «flags» — дополнительные флаги, определяющие, как будет работать регулярное выражение. Например, флаг i указывает, что регулярное выражение должно игнорировать регистр символов.

Вот пример использования конструктора RegExp() для создания регулярного выражения, которое ищет все цифры в строке:

const regex = new RegExp("\\d", "g");

Здесь мы передаём строку \d в качестве первого аргумента, что означает «любая цифра». Флаг g указывает, что регулярное выражение должно искать все совпадения в строке.

Этот способ предпочтительнее, когда вы знаете, что выражение будет изменяться или вы не знаете шаблон. Например, это информация, которую вводит пользователь в формах.

2. Литералы

Литерал регулярного выражения, так же как и конструктор, состоит из двух частей. Первая часть — шаблон, который необходимо описать, он заключается в слеши (//). Вторая часть, после закрывающего слеша, — флаги, но их использование также необязательно.

Вот как он выглядит:

const regex = /pattern/flags;

Разберём на примере использования литерала для создания того же регулярного выражения, которое ищет все цифры в строке:

const regex = /\d/g;

Здесь мы используем литерал /\d/g, который означает «любая цифра» с флагом g, указывающим, что регулярное выражение должно искать все совпадения в строке.

Обратите внимание: если вы планируете создавать регулярные выражения при помощи литералов, такой подход не позволяет изменять задаваемые значения динамически — литералы регулярных выражений компилируются заранее при анализе скрипта. Зато если ваше регулярное выражение постоянно, то литералы позволяют увеличить производительность.

Оба эти способа создания регулярных выражений в JavaScript эквивалентны друг другу — выбирайте тот, который вам удобнее.

Базовый синтаксис регулярных выражений в JavaScript

Разберём основу синтаксиса регулярных выражений в JavaScript.

Шаблоны и метасимволы

Шаблоны — набор символов, которые используют для поиска определённых сочетаний символов в тексте. Метасимволы — специальные символы, которые используют для составления шаблонов.

Вот несколько примеров шаблонов и метасимволов:

. — соответствует любому одиночному символу, кроме символа новой строки.

* — соответствует нулю или более повторениям предыдущего символа.

+ — соответствует одному или более повторениям предыдущего символа.

? — соответствует нулю или одному повторению предыдущего символа.

^ — соответствует началу строки.

$ — соответствует концу строки.

() — определяет группу символов, которые могут быть повторены.

Квантификаторы и модификаторы

Квантификаторы — метасимволы, которые определяют количество повторений предыдущего символа. 

Некоторые квантификаторы:

{n} — соответствует ровно n повторениям предыдущего символа.

{n,m} — соответствует от n до m повторений предыдущего символа.

{n,} — соответствует n или более повторениям предыдущего символа.

? — делает предыдущий квантификатор ленивым, т. е. соответствует наименьшему возможному количеству повторений.

Пример использования квантификатора:

const pattern = /a{3}/; // соответствует трём символам "a" подряд

В тексте, который будет обрабатываться с помощью этого регулярного выражения, будут найдены все вхождения трёх символов a подряд. Например, строка «baaaad» будет соответствовать шаблону, а строка «abaa» — нет.

Модификаторы — специальные флаги, которые можно добавить к регулярному выражению для изменения его поведения. 

Некоторые модификаторы:

g — глобальный поиск, ищет все совпадения в строке.

i — игнорирование регистра, игнорирует различия между верхним и нижним регистром.

m — многострочный поиск, позволяет искать совпадения в нескольких строках.

Пример использования модификатора:

const pattern = /test/gi; // соответствует строкам "test" или "Test" или "TEST" глобально и без учёта регистра

Группы и обратные ссылки

Группы — последовательности символов, заключённые в скобки (), которые могут повторяться с помощью квантификаторов. Группы также позволяют использовать обратные ссылки, которые ссылаются на результат совпадения группы в регулярном выражении. Обратная ссылка обозначается символом \ и номером группы, например, \1 ссылается на первую группу.

Пример использования групп и обратных ссылок:

​​const pattern = /(\w+)\s\1/; // соответствует повторяющейся последовательности символов, разделённой пробелом

В этом примере группа (\w+) соответствует любой последовательности символов, состоящей из буквенно-цифровых символов и подчёркивания. Затем \s соответствует пробелу, а \1 обратно ссылается на первую группу (\w+), так что регулярное выражение соответствует только тем строкам, в которых последовательность символов повторяется через пробел.

Экранирование в регулярных выражениях

Как мы разобрались выше, в регулярках есть символы, которые имеют специальное значение и используются для обозначения шаблонов, таких как поиск символов, чисел и т. д. Но иногда возникают ситуации, когда эти символы должны быть экранированы — использованы как обычные символы, вместо своего специального значения.

Для экранирования символа используется обратный слеш \

Например, символ . в регулярном выражении означает любой символ, но если мы хотим использовать точку как обычный символ, мы можем экранировать его, написав \.

Примеры:

  • Поиск всех цифр в тексте.
			let str = "Моя любимая цифра — 7, а у моего друга — 3.";
const regex = /\d/g; // экранируем метасимвол "\d", который обозначает цифру
let result = str.match(regex);
console.log(result); // ["7", "3"]
		
  • Замена всех пробелов на символ подчёркивания.
			let str = "Это пример строки с несколькими пробелами.";
const regex = /\s/g; // экранируем метасимвол "\s", который обозначает пробел
let result = str.replace(regex, "_");
console.log(result); // Это_пример_строки_с_несколькими_пробелами.
		
  • Замена всех точек в тексте на запятые, кроме точек, которые находятся внутри чисел.
			let str = "По отчёту за 2022 год выручка составила 12.345.678 рублей.";
const regex = /(?<!\d)\.|\.(?!\d)/g; // экранируем точку, используем негативный и позитивный просмотр вперёд и назад для исключения точек, находящихся внутри чисел
let result = str.replace(regex, ",");
console.log(result); // По отчёту за 2022 год выручка составила 12,345,678 рублей.
		

Как использовать регулярки с методами объекта RegExp и String

После того как базовый синтаксис и способы написания шаблонов усвоены, перейдём к использованию регулярок в работе. Для работы с регулярными выражениями в JavaScript есть несколько методов — рассмотрим каждый.

Метод test()

Метод test() проверяет, соответствует ли регулярное выражение заданной строке. Метод возвращает true, если строка соответствует регулярному выражению, и false, если не соответствует.

Пример использования метода test():

			let str = "Hello, world!";
const pattern = /Hello/;

if (pattern.test(str)) {
  console.log("String contains 'Hello'");
} else {
  console.log("String does not contain 'Hello'");
}
		

В этом примере мы используем метод test() для проверки, содержит ли строка str подстроку «Hello». Регулярное выражение /Hello/ соответствует строке «Hello», и метод test() вернёт true.

Метод exec()

Метод exec() используется для поиска совпадений регулярного выражения в заданной строке. Метод возвращает массив, содержащий найденное совпадение и дополнительную информацию о нём.

Пример использования метода exec():

			let str = "Hello, world!";
const pattern = /Hello/;

let result = pattern.exec(str);

console.log(result); // ["Hello", index: 0, input: "Hello, world!"]
		

В этом примере мы используем метод exec() для поиска совпадений регулярного выражения /Hello/ в строке str. Метод exec() возвращает массив, содержащий найденное совпадение «Hello», индекс первого символа совпадения в исходной строке и саму исходную строку.

Метод match()

Метод match() используется для поиска всех совпадений регулярного выражения в заданной строке. Метод возвращает массив, содержащий все найденные совпадения.

Пример использования метода match():

			let str = "The quick brown fox jumps over the lazy dog.";
const pattern = /the/gi;

let result = str.match(pattern);

console.log(result); // ["the", "the"]
		

В этом примере мы используем метод match() для поиска всех совпадений регулярного выражения /the/gi в строке str. Метод match() возвращает массив, содержащий все найденные совпадения, включая повторения.

Метод replace()

Метод replace() принимает два аргумента: регулярное выражение и строку, на которую нужно заменить найденное совпадение. Этот метод ищет все совпадения с заданным регулярным выражением в исходной строке и заменяет их на указанную строку.

Пример использования метода replace():

			let str = "Привет, мир!";
const pattern = /мир/;

let newstr = str.replace(pattern, "земля");

console.log(newstr); // "Привет, земля!"
		

В этом примере мы используем метод replace() для замены слова «мир» на «земля» в строке «Привет, мир!» Регулярное выражение /мир/ ищет все совпадения со словом «мир» в строке, и метод replace() заменяет их на слово «земля». Результатом работы метода является новая строка «Привет, земля!»

Метод search()

Метод search() принимает один аргумент — регулярное выражение. Он ищет первое совпадение с заданным регулярным выражением в исходной строке и возвращает индекс первого символа совпадения. Если совпадение не найдено, метод возвращает -1.

Например:

			let str = "Это текст для примера";
const pattern = /текст/;

let index = str.search(pattern);

console.log(index); // 4
		

В этом примере мы используем метод search() для поиска первого совпадения со словом «текст» в строке «Это текст для примера». Регулярное выражение /текст/ ищет первое совпадение со словом «текст» в строке, и метод search() возвращает индекс первого символа этого совпадения, который равен 4.

Метод split()

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

Пример использования метода split():

			let str = "Это текст для примера";
const pattern = / /;

let words = str.split(pattern);

console.log(words); // ["Это", "текст", "для", "примера"]
		

В этом примере мы используем метод split() для разделения строки «Это текст для примера» на массив слов, используя регулярное выражение / / в качестве разделителя. Регулярное выражение / / ищет все пробелы в строке, и метод split() использует их в качестве разделителя для разделения строки на массив слов. Результатом работы метода является массив [«Это», «текст», «для», «примера»].

Когда и как использовать регулярки на практике

Регулярные выражения пригодятся для самых разных задач: поиска текста, валидации данных, замены текста, разбора текста на составляющие, генерации паролей и т. д.

Разберём подробнее несколько вариантов использования.

Валидация email-адресов

Один из популярных примеров использования регулярных выражений — валидация email-адресов.

Шаблон будет выглядеть так:

/^[^\s@]+@[^\s@]+\.[^\s@]+$/, где

^ — начало строки

@ — символ «@»

\. — символ «.» (экранированный с помощью слеша)

$ — конец строки

Таким образом, этот паттерн соответствует любому email-адресу, который состоит из локальной части (часть до символа «@») и доменной части (часть после символа «@» и до символа «.») с верным форматом.

Для проверки email-адреса на соответствие шаблону можно использовать метод test():

			function validateEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

console.log(validateEmail("user@example.com")); // true
console.log(validateEmail("invalid@.com")); // false
console.log(validateEmail("invalidemail.com")); // false
		

В этом примере функция validateEmail() принимает email-адрес в качестве аргумента и использует регулярное выражение для проверки его соответствия формату. Метод test() возвращает true, если email-адрес соответствует паттерну, и false в противном случае.

Форматирование текста

Регулярные выражения могут быть полезны для форматирования текста, например для замены повторяющихся символов, форматирования дат и т. д. 

Рассмотрим пример форматирования даты с использованием регулярных выражений.

Допустим, у нас есть дата в формате «гггг-мм-дд» (например, «2023-03-29»), а мы хотим преобразовать её в формат «дд.мм.гггг» (например, «29.03.2023»). Для этого мы можем использовать регулярное выражение, чтобы разделить дату на отдельные части, а затем использовать эти части для создания новой строки в нужном формате.

			let date = "2023-03-29";
const regex = /^(\d{4})-(\d{2})-(\d{2})$/;
let match = date.match(regex);
let formattedDate = match[3] + "." + match[2] + "." + match[1];
console.log(formattedDate); // "29.03.2023"
		

Давайте разберём это регулярное выражение:

^ — начало строки

(\d{4}) — группа, соответствующая 4 цифрам (год)

— — символ «-«

(\d{2}) — группа, соответствующая 2 цифрам (месяц)

— — символ «-«

(\d{2}) — группа, соответствующая 2 цифрам (день)

$ — конец строки

В этом примере мы используем метод match() для поиска соответствия регулярному выражению в строке date. Если соответствие найдено, метод match() возвращает массив, содержащий соответствующие группы в порядке их появления в регулярном выражении. Затем мы используем эти группы для создания новой строки в нужном формате.

Регулярные выражения также могут быть полезны для форматирования других типов данных, например, для замены повторяющихся символов или форматирования номеров телефонов.

Извлечение номеров телефонов из строки

Регулярные выражения широко используются для извлечения информации из текстовых строк. Например, если есть строка, содержащая номера телефонов, можно использовать регулярные выражения, чтобы извлечь эти номера телефонов и сохранить их в базе данных. 

Рассмотрим несколько примеров использования регулярных выражений для извлечения информации из строк.

Допустим, у вас есть строка, содержащая несколько номеров телефонов. Вы хотите извлечь эти номера телефонов из строки и сохранить их в базе данных. 

Для этого вы можете использовать следующий код:

			const string = "Мой номер телефона: +7 (999) 123-45-67. Номер телефона моего друга: +7 (987) 654-32-10.";
const regex = /\+\d\s?\(\d{3}\)\s?\d{3}-\d{2}-\d{2}/g;
const phoneNumbers = string.match(regex);

console.log(phoneNumbers);
// Output: ["+7 (999) 123-45-6	7", "+7 (987) 654-32-10"]
		

В этом примере мы используем регулярное выражение /\+\d\s?\(\d{3}\)\s?\d{3}-\d{2}-\d{2}/g, чтобы извлечь номера телефонов из строки string.

Регулярное выражение ищет символ «+», и за ним следует одна или несколько цифр, за которыми следует необязательный пробел. Затем идут открывающая и закрывающая скобки, за которыми следуют три цифры, после чего снова идёт необязательный пробел. Далее идут три цифры, тире, две цифры, тире и ещё две цифры. В результате, если в строке есть номер телефона в формате, соответствующем регулярному выражению, он будет извлечён и сохранён в массиве phoneNumbers.

Ещё несколько советов по использованию регулярок

  • Пишите максимально простые регулярные выражения и не усложняйте код без необходимости. Чем проще регулярное выражение, тем быстрее и эффективнее оно будет работать.
  • Тестируйте регулярные выражения перед использованием. Это позволит выявить возможные ошибки или проблемы в выражении и сделать необходимые исправления. Используйте онлайн-сервисы, такие как regex101.com, regexr.com, regexplanet.com и подобные. 
  • Не злоупотребляйте использованием регулярок. Иногда другие простые методы могут оказаться более эффективными и лёгкими в понимании. 
  • Используйте комментарии и форматирование кода, чтобы облегчить его чтение и обслуживание. Регулярные выражения могут быть очень сложными для понимания. 
  • Используйте библиотеки. Есть множество библиотек уже готовых шаблонов — часто этого достаточно для решения задачи.

Заключение

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

Следите за новыми постами
Следите за новыми постами по любимым темам
8К открытий10К показов