Пошук уроків, статей та іншого контенту
Розберете іменовані групи та зворотні посилання, щоб зробити складні регулярні вирази зрозумілішими.
Регулярні вирази часто використовують групи захоплення:
const pattern = /(\d{4})-(\d{2})-(\d{2})/;
const match = "2026-07-26".match(pattern);
console.log(match[1]); // "2026"
console.log(match[2]); // "07"
console.log(match[3]); // "26"Такі групи працюють, але з часом вираз стає менш зрозумілим. За значеннями match[1], match[2] і match[3] не завжди очевидно, що саме вони містять.
Іменовані групи дають змогу привласнити групі зрозуміле ім’я:
const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = "2026-07-26".match(pattern);
console.log(match.groups.year); // "2026"
console.log(match.groups.month); // "07"
console.log(match.groups.day); // "26"Іменовані групи особливо корисні, коли:
регулярний вираз містить багато груп;
порядок груп може змінитися;
результат потрібно передати в інший код;
вираз перевіряє структуру складного рядка;
значення групи потрібно повторно використати у виразі.
Іменована група записується так:
(?<name>pattern)де:
name — ім’я групи;
pattern — регулярний вираз усередині групи.
Наприклад:
const pattern = /Hello, (?<userName>\w+)!/;
const match = "Hello, Olena!".match(pattern);
console.log(match.groups.userName); // "Olena"Ім’я групи має бути коректним ідентифікатором JavaScript. Зазвичай використовують літери, цифри, символ _, але ім’я не повинно починатися з цифри.
const valid = /(?<firstName>[A-Za-z]+)/;Після успішного збігу об’єкт match містить властивість groups. Вона містить об’єкт із названими групами:
const pattern = /(?<protocol>https?):\/\/(?<host>[^/]+)/;
const match = "https://example.com/docs".match(pattern);
console.log(match.groups);
// { protocol: "https", host: "example.com" }Якщо регулярний вираз не знайшов збіг, match матиме значення null:
const pattern = /ID: (?<id>\d+)/;
const match = "No identifier".match(pattern);
console.log(match); // nullТому перед доступом до match.groups потрібно перевірити, чи існує збіг.
Іменована група залишається звичайною групою захоплення. Вона доступна і за числовим індексом, і за іменем:
const pattern = /(?<area>\d{3})-(\d{2})/;
const match = "123-45".match(pattern);
console.log(match[1]); // "123"
console.log(match[2]); // "45"
console.log(match.groups.area); // "123"Однак для читабельності краще використовувати ім’я, якщо воно є:
const areaCode = match.groups.area;
const subscriberNumber = match[2];Безіменну групу можна залишити для простих внутрішніх операцій. Якщо ж значення групи використовується в програмі, іменування зазвичай робить код зрозумілішим.
Зворотне посилання дає змогу перевірити, що в іншій частині рядка повторюється саме той текст, який було захоплено групою.
Для іменованої групи використовується синтаксис:
\k<name>Наприклад, вираз перевіряє HTML-подібний тег, у якого відкривальний і закривальний тег мають однакову назву:
const pattern = /<(?<tag>[a-z]+)>.*?<\/\k<tag>>/;
console.log(pattern.test("<article>Text</article>")); // true
console.log(pattern.test("<article>Text</section>")); // falseРозглянемо вираз по частинах:
<(?<tag>[a-z]+)> # відкривальний тег і його назва
.*? # найкоротший можливий текст усередині
<\/\k<tag>> # закривальний тег із тією самою назвоюУ \k<tag> підставляється фактичне значення групи tag. Якщо на початку було захоплено article, наприкінці очікується саме article.
У JavaScript коментарі не можна записувати безпосередньо всередині звичайного регулярного виразу. Наведений поділ на частини призначений лише для пояснення.
Можна перевірити, що рядок починається і закінчується однаковим типом лапок:
const quotedText = /(?<quote>["'])(?<content>.*?)\k<quote>/;
console.log(quotedText.test('"JavaScript"')); // true
console.log(quotedText.test("'JavaScript'")); // true
console.log(quotedText.test('"JavaScript\'')); // falseГрупа quote захоплює або подвійні, або одинарні лапки. Зворотне посилання вимагає, щоб у кінці була така сама лапка.
Це надійніше, ніж вираз:
/["'].*?["]/Останній варіант дозволив би відкрити рядок подвійною лапкою, а закрити одинарною.
До звичайних груп можна звертатися за номером:
\1
\2
\3Приклад:
const pattern = /(["']).*?\1/;
console.log(pattern.test('"text"')); // true
console.log(pattern.test("'text'")); // true
console.log(pattern.test('"text\'')); // falseТут (["']) — перша група, тому зворотне посилання на неї записано як \1.
Іменована форма:
const namedPattern = /(?<quote>["']).*?\k<quote>/;робить намір виразу очевиднішим. Якщо в регулярному виразі додати або переставити групи, числові посилання легко зламати. Ім’я групи не залежить від її номера.
Не кожна група має зберігати результат. Для групування без захоплення використовують синтаксис:
(?:pattern)Наприклад:
const pattern = /(?:https?):\/\/(?<host>[^/]+)/;
const match = "https://example.com/path".match(pattern);
console.log(match.groups.host); // "example.com"
console.log(match[1]); // "example.com"(?:https?) лише групує альтернативу http або https, але не створює окремого результату.
Це допомагає:
не збільшувати кількість числових груп;
не створювати непотрібні значення в результаті;
зменшити ризик плутанини з номерами груп.
Іменована група може бути частиною необов’язкового фрагмента:
const pattern = /(?<protocol>https?:\/\/)?(?<host>[\w.-]+)/;
const withProtocol = "https://example.com".match(pattern);
const withoutProtocol = "example.com".match(pattern);
console.log(withProtocol.groups);
// { protocol: "https://", host: "example.com" }
console.log(withoutProtocol.groups);
// { protocol: undefined, host: "example.com" }Група protocol існує в обох випадках, але якщо необов’язковий фрагмент не збігся, її значенням буде undefined.
Перевіряти такі значення можна за допомогою умов:
function parseHost(value) {
const pattern = /(?<protocol>https?:\/\/)?(?<host>[\w.-]+)/;
const match = value.match(pattern);
if (!match) {
return null;
}
return {
protocol: match.groups.protocol ?? "http://",
host: match.groups.host,
};
}
console.log(parseHost("https://example.com"));
// { protocol: "https://", host: "example.com" }
console.log(parseHost("example.com"));
// { protocol: "http://", host: "example.com" }replaceІменовані групи можна використовувати не лише для перевірки, а й під час заміни.
У рядку заміни іменована група позначається так:
$<name>Наприклад:
const date = "2026-07-26";
const result = date.replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
"$<day>.$<month>.$<year>"
);
console.log(result); // "26.07.2026"Також можна передати функцію заміни. Іменовані групи будуть доступні в останньому аргументі — об’єкті groups:
const date = "2026-07-26";
const result = date.replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
(fullMatch, year, month, day, offset, originalString, groups) => {
return `${groups.day}.${groups.month}.${groups.year}`;
}
);
console.log(result); // "26.07.2026"На практиці з функцією заміни краще не покладатися на позиції аргументів, якщо регулярний вираз складний. Названі групи роблять логіку зрозумілішою:
const input = "2026-07-26";
const output = input.replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
(_, __, ___, ____, _____, ______, groups) =>
`${groups.day}/${groups.month}/${groups.year}`
);
console.log(output); // "26/07/2026"Проте велика кількість технічних аргументів у такій функції може погіршувати читабельність. Якщо заміна проста, рядок $<name> часто є кращим варіантом.
Розглянемо рядок із журналу вебсервера:
2026-07-26 14:35:09 [ERROR] Database connection failedПотрібно отримати дату, час, рівень повідомлення та текст.
const logLine =
"2026-07-26 14:35:09 [ERROR] Database connection failed";
const logPattern =
/^(?<date>\d{4}-\d{2}-\d{2})\s+(?<time>\d{2}:\d{2}:\d{2})\s+\[(?<level>[A-Z]+)\]\s+(?<message>.+)$/;
const match = logLine.match(logPattern);
if (match) {
const { date, time, level, message } = match.groups;
console.log({
date,
time,
level,
message,
});
} else {
console.log("Рядок має неправильний формат");
}Результат:
{
date: "2026-07-26",
time: "14:35:09",
level: "ERROR",
message: "Database connection failed"
}Такий код легше підтримувати, ніж варіант із match[1], match[2], match[3] і match[4].
gПід час використання match із прапорцем g JavaScript повертає лише повні збіги, без груп захоплення:
const pattern = /(?<word>[A-Za-z]+)/g;
const matches = "One two three".match(pattern);
console.log(matches); // ["One", "two", "three"]Щоб отримати іменовані групи для кожного збігу, використовуйте matchAll:
const pattern = /(?<word>[A-Za-z]+)/g;
const matches = [..."One two three".matchAll(pattern)];
for (const match of matches) {
console.log(match.groups.word);
}
// One
// two
// threematchAll повертає ітератор, тому його часто перетворюють на масив за допомогою оператора розгортання ....
Приклад із розбором пар ключ-значення:
const input = "name=Olena age=28 role=developer";
const pattern = /(?<key>[a-z]+)=(?<value>[A-Za-z0-9]+)/g;
for (const match of input.matchAll(pattern)) {
console.log(`${match.groups.key}: ${match.groups.value}`);
}
// name: Olena
// age: 28
// role: developerdУ сучасному JavaScript прапорець d дає змогу отримати індекси збігів. Для іменованих груп ці індекси доступні через indices.groups:
const pattern = /(?<protocol>https?):\/\/(?<host>[^/]+)/d;
const match = "https://example.com/docs".match(pattern);
console.log(match.indices.groups.protocol); // [0, 5]
console.log(match.indices.groups.host); // [8, 19]Це корисно, якщо потрібно не лише отримати текст, а й дізнатися його позицію в початковому рядку.
Підтримка сучасних можливостей регулярних виразів залежить від середовища виконання. Якщо код запускається в застарілому браузері або старій версії Node.js, сумісність потрібно перевірити окремо.
У межах одного регулярного виразу імена груп зазвичай мають бути унікальними:
// Помилка: дві групи мають однакове ім’я
const pattern = /(?<value>\d+)-(?<value>\d+)/;Не варто створювати імена, які конфліктують із властивостями об’єкта або погіршують читабельність. Краще використовувати конкретні назви:
/(?<firstName>[A-Za-z]+)\s+(?<lastName>[A-Za-z]+)/замість:
/(?<a>[A-Za-z]+)\s+(?<b>[A-Za-z]+)/Іменування не робить регулярний вираз автоматично простим. Якщо вираз має багато вкладених умов, альтернатив і квантифікаторів, його все одно варто розбити на зрозумілі частини або замінити звичайним кодом.
groups без перевірки збігуconst match = "invalid".match(/ID: (?<id>\d+)/);
// Помилка, якщо match дорівнює null
console.log(match.groups.id);Безпечний варіант:
if (match) {
console.log(match.groups.id);
}Або:
const id = match?.groups?.id;Оголошення групи:
(?<name>pattern)Зворотне посилання:
\k<name>(?<name>...) захоплює новий текст, а \k<name> порівнює текст із тим, що вже було захоплено.
$<name> усередині регулярного виразу$<name> використовується в рядку заміни:
"2026-07-26".replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
"$<day>.$<month>.$<year>"
);У самому регулярному виразі зворотне посилання має вигляд \k<name>:
/(?<quote>["']).*?\k<quote>/g збереже групи в результаті matchconst result = "one two".match(/(?<word>\w+)/g);
console.log(result);
// ["one", "two"], а не об'єкти з groupsДля груп використовуйте matchAll:
for (const match of "one two".matchAll(/(?<word>\w+)/g)) {
console.log(match.groups.word);
}.*Вираз на кшталт:
/(?<open><\w+>).*?(?<close><\/\w+>)/захоплює відкривальний і закривальний фрагменти, але не перевіряє, що назви тегів однакові.
Якщо потрібно порівняти назви, захопіть її один раз і використайте зворотне посилання:
/<(?<tag>\w+)>.*?<\/\k<tag>>//Якщо регулярний вираз створюється літералом JavaScript і всередині потрібно знайти символ /, його треба екранувати:
/<(?<tag>\w+)>.*?<\/\k<tag>>/Якщо використовується конструктор RegExp, правила запису рядка інші:
const pattern = new RegExp(
"<(?<tag>\\w+)>.*?<\\/\\k<tag>>"
);У рядку JavaScript зворотний слеш потрібно додатково екранувати.
Іменована група має синтаксис (?<name>pattern).
Значення іменованих груп доступні через match.groups.name.
Зворотне посилання на іменовану групу записується як \k<name>.
Іменовані групи роблять складні регулярні вирази зрозумілішими за числові посилання \1, \2 і подібні.
Для групування без захоплення використовуйте (?:pattern).
Необов’язкова група може мати значення undefined.
Для пошуку багатьох збігів разом з іменованими групами використовуйте matchAll із прапорцем g.
У рядку заміни іменована група позначається як $<name>.
Перед доступом до groups перевіряйте, чи регулярний вираз справді знайшов збіг.