16 августа 2023 г.

Введение: шаблоны и флаги

Регулярные выражения – мощное средство поиска и замены в строке.

В JavaScript регулярные выражения реализованы отдельным объектом RegExp и интегрированы в методы строк.

Регулярные выражения

Регулярное выражение (оно же «регэксп», «регулярка» или просто «рег»), состоит из шаблона (также говорят «паттерн») и необязательных флагов.

Существует два синтаксиса для создания регулярного выражения.

«Длинный» синтаксис:

regexp = new RegExp("шаблон", "флаги");

…И короткий синтаксис, использующий слеши "/":

regexp = /шаблон/; // без флагов
regexp = /шаблон/gmi; // с флагами gmi (будут описаны далее)

Слеши /.../ говорят JavaScript о том, что это регулярное выражение. Они играют здесь ту же роль, что и кавычки для обозначения строк.

Регулярное выражение regexp в обоих случаях является объектом встроенного класса RegExp.

Основная разница между этими двумя способами создания заключается в том, что слеши /.../ не допускают никаких вставок переменных (наподобие возможных в строках через ${...}). Они полностью статичны.

Слеши используются, когда мы на момент написания кода точно знаем, каким будет регулярное выражение – и это большинство ситуаций. А new RegExp – когда мы хотим создать регулярное выражение «на лету» из динамически сгенерированной строки, например:

let tag = prompt("Какой тег вы хотите найти?", "h2");

let regexp = new RegExp(`<${tag}>`); // то же, что /<h2>/  при ответе "h2" на prompt выше

Флаги

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

В JavaScript их всего семь:

i
С этим флагом поиск не зависит от регистра: нет разницы между A и a (см. пример ниже).
g
С этим флагом поиск ищет все совпадения, без него – только первое.
m
Многострочный режим (рассматривается в главе Многострочный режим якорей ^ $, флаг "m").
s
Включает режим «dotall», при котором точка . может соответствовать символу перевода строки \n (рассматривается в главе Символьные классы).
u
Включает полную поддержку Юникода. Флаг разрешает корректную обработку суррогатных пар (подробнее об этом в главе Юникод: флаг "u" и класс \p{...}).
y
Режим поиска на конкретной позиции в тексте (описан в главе Поиск на заданной позиции, флаг "y").
d
С этим флагом результат регулярного выражения помещается в массив, который содержит дополнительную информацию о регулярном выражении, например индексы начала и конца подстрок. Этот флаг не меняет поведение регулярного выражения, а лишь предоставляет дополнительную информацию.
Цветовые обозначения

Здесь и далее в тексте используется следующая цветовая схема:

  • регулярное выражение – красный
  • строка (там где происходит поиск) – синий
  • результат – зелёный

Поиск: str.match

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

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

У него есть три режима работы:

  1. Если у регулярного выражения есть флаг g, то он возвращает массив всех совпадений:

    let str = "Любо, братцы, любо!";
    
    alert( str.match(/любо/gi) ); // Любо,любо (массив из 2х подстрок-совпадений)

    Обратите внимание: найдены и Любо и любо, благодаря флагу i, который делает регулярное выражение регистронезависимым.

  2. Если такого флага нет, то возвращает только первое совпадение в виде массива, в котором по индексу 0 находится совпадение, и есть свойства с дополнительной информацией о нём:

    let str = "Любо, братцы, любо!";
    
    let result = str.match(/любо/i); // без флага g
    
    alert( result[0] );     // Любо (первое совпадение)
    alert( result.length ); // 1
    
    // Дополнительная информация:
    alert( result.index );  // 0 (позиция совпадения)
    alert( result.input );  // Любо, братцы, любо! (исходная строка)

    В этом массиве могут быть и другие индексы, кроме 0, если часть регулярного выражения выделена в скобки. Мы разберём это в главе Скобочные группы.

  3. И, наконец, если совпадений нет, то, вне зависимости от наличия флага g, возвращается null.

    Это очень важный нюанс. При отсутствии совпадений возвращается не пустой массив, а именно null. Если об этом забыть, можно легко допустить ошибку, например:

    let matches = "JavaScript".match(/HTML/); // = null
    
    if (!matches.length) { // Ошибка: у null нет свойства length
      alert("Ошибка в строке выше");
    }

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

    let matches = "JavaScript".match(/HTML/) || [];
    
    if (!matches.length) {
      alert("Совпадений нет"); // теперь работает
    }

Замена: str.replace

Метод str.replace(regexp, replacement) заменяет совпадения с regexp в строке str на replacement (все, если есть флаг g, иначе только первое).

Например:

// без флага g
alert( "We will, we will".replace(/we/i, "I") ); // I will, we will

// с флагом g
alert( "We will, we will".replace(/we/ig, "I") ); // I will, I will

В строке замены replacement мы можем использовать специальные комбинации символов для вставки фрагментов совпадения:

Спецсимволы Действие в строке замены
$& вставляет всё найденное совпадение
$` вставляет часть строки до совпадения
$' вставляет часть строки после совпадения
$n если n это 1-2 значное число, вставляет содержимое n-й скобочной группы регулярного выражения, больше об этом в главе Скобочные группы
$<name> вставляет содержимое скобочной группы с именем name, также изучим в главе Скобочные группы
$$ вставляет символ "$"

Пример с $&:

alert( "Люблю HTML".replace(/HTML/, "$& и JavaScript") ); // Люблю HTML и JavaScript

Проверка: regexp.test

Метод regexp.test(str) проверяет, есть ли хоть одно совпадение, если да, то возвращает true, иначе false.

let str = "Я ЛюБлЮ JavaScript";
let regexp = /люблю/i;

alert( regexp.test(str) ); // true

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

Полная информация о различных методах дана в главе Методы RegExp и String.

Итого

  • Регулярное выражение состоит из шаблона и необязательных флагов: g, i, m, u, s, y, d.
  • Без флагов и специальных символов, которые мы изучим позже, поиск по регулярному выражению аналогичен поиску подстроки.
  • Метод str.match(regexp) ищет совпадения: все, если есть флаг g, иначе только первое.
  • Метод str.replace(regexp, replacement) заменяет совпадения с regexp на replacement: все, если у регулярного выражения есть флаг g, иначе только первое.
  • Метод regexp.test(str) возвращает true, если есть хоть одно совпадение, иначе false.
Карта учебника