Создание

const re1 = /pattern/flags;
const re2 = new RegExp('pattern', 'flags'); // когда паттерн динамический

Флаги

ФлагЗначение
gglobal — все совпадения, не только первое
iignore case
mmultiline — ^/$ работают по строкам, а не по всей строке
sdotAll — . матчит и \n тоже
uunicode-режим (важно для эмодзи, суррогатных пар)
ysticky — матч строго с позиции lastIndex

Классы символов

ПаттернЗначение
.любой символ кроме \n (если нет флага s)
\d / \Dцифра / не цифра
\w / \W[A-Za-z0-9_] / наоборот
\s / \Sпробельный / непробельный
[abc]один из перечисленных
[^abc]любой, кроме перечисленных
[a-z], [0-9], [A-Za-z]диапазон

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

ПаттернЗначение
*0 или больше
+1 или больше
?0 или 1
{n}ровно n
{n,}n или больше
{n,m}от n до m
*?, +?, ??, {n,m}?lazy-версии (минимально возможное совпадение)

Greedy vs Lazy:

"<a><b>".match(/<.+>/)   // ['<a><b>']  — greedy, всё целиком
"<a><b>".match(/<.+?>/)  // ['<a>']     — lazy, минимально

Якоря и границы

ПаттернЗначение
^начало строки (или начало после \n с флагом m)
$конец строки
\bграница слова
\BНЕ граница слова

Группы

ПаттернЗначение
(...)захватывающая группа
(?:...)незахватывающая (не попадает в результат)
(?<name>...)именованная группа
\1, \2обратная ссылка на группу 1, 2 (внутри паттерна)
(?=...)lookahead — “за чем следует”
(?!...)negative lookahead
(?<=...)lookbehind — “перед чем стоит”
(?<!...)negative lookbehind
// lookahead пример: число с разделителями тысяч
"1234567".replace(/\B(?=(\d{3})+(?!\d))/g, ',') // "1,234,567"

Альтернация

/cat|dog/        // "cat" или "dog"
/colou?r/        // "color" или "colour"

Экранирование спецсимволов

Нужно экранировать: . * + ? ^ $ { } ( ) | [ ] \

const escaped = str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

Основные методы строк/RegExp

МетодВозвращаетКогда использовать
str.match(re)массив совпадений или nullбез g — первое + группы; с g — все совпадения без групп
str.matchAll(re)итератор (нужен флаг g)все совпадения + группы для каждого
str.replace(re, str|fn)новая строказамена (с g — все вхождения)
str.replaceAll(re, ...)новая строкатребует флаг g у regex
str.split(re)массив строкразбиение по паттерну
str.search(re)индекс или -1позиция первого совпадения
re.test(str)booleanпросто проверка наличия
re.exec(str)массив или nullпошаговый перебор (с g двигает lastIndex)

replace с callback и группами

"backgroundColor".replace(/[A-Z]/g, m => '-' + m.toLowerCase())
// "background-color"
 
"2024-06-20".replace(/(\d+)-(\d+)-(\d+)/, '$3.$2.$1')
// "20.06.2024"
 
// именованные группы в replace
"2024-06-20".replace(/(?<y>\d+)-(?<m>\d+)-(?<d>\d+)/, '$<d>.$<m>.$<y>')

Частые шаблоны-заготовки

const EMAIL = /^[\w.+-]+@[\w-]+\.[a-z]{2,}$/i;
const URL_SIMPLE = /^https?:\/\/[\w.-]+\.[a-z]{2,}(\/\S*)?$/i;
const INTEGER = /^[+-]?\d+$/;
const FLOAT = /^[+-]?\d+(\.\d+)?$/;
const HEX_COLOR = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
const WHITESPACE_TRIM = /^\s+|\s+$/g;
const MULTI_SPACE = /\s+/g;
const WORD_BOUNDARY_SPLIT = /\b/;

Частые ловушки

  • match() без g возвращает группы, с g — нет. Если нужны и все совпадения, и группы — используй matchAll().
  • exec() в цикле с флагом g использует lastIndex — забыл сбросить или не использовал g → бесконечный цикл или вечный один и тот же результат.
  • . не матчит перенос строки без флага s.
  • ^/$ без флага m относятся ко всей строке, а не к каждой линии.
  • Жадные квантификаторы (*, +) могут “захватить” больше, чем ожидаешь — проверяй на lazy (*?, +?).
  • Regex не умеет считать вложенность (скобки, теги) — для этого нужен парсер/стек (см. LeetCode 394, 10).

regexp