Пошук уроків, статей та іншого контенту
Освоїте групування, альтернативи та побудову складних шаблонів із кількох можливих варіантів.
Групи дають змогу об’єднати кілька символів або підвиразів в одну логічну частину шаблону. Для створення групи використовують круглі дужки:
(...)Наприклад, шаблон /(ab)+/ знаходить одну або більше послідовностей ab:
console.log("ab".match(/(ab)+/)); // ["ab"]
console.log("abab".match(/(ab)+/)); // ["abab"]
console.log("aba".match(/(ab)+/)); // ["ab"]Без групи квантифікатор + застосовувався б лише до попереднього символу:
console.log("abab".match(/ab+/)); // ["abb"?] або ["ab"] залежно від рядкаУ виразі /ab+/:
a має з’явитися один раз;
b+ означає одну або більше літер b;
послідовність ab не повторюється цілком.
Тому групування важливе, коли квантифікатор має стосуватися кількох символів.
Припустімо, номер телефону може містити код країни +380:
const pattern = /(\+380)?\s?\d{2}\s?\d{3}\s?\d{2}\s?\d{2}/;
console.log(pattern.test("+380 67 123 45 67")); // true
console.log(pattern.test("67 123 45 67")); // trueГрупа (\+380) об’єднує символи +, 3, 8, 0, а ? робить всю групу необов’язковою.
Якби знак питання стояв після 0, це означало б необов’язковість лише останнього символу:
\+380?Такий шаблон відповідає +38 або +380, але не робить необов’язковим увесь код країни.
Символ | означає «або». Він дає змогу описати кілька можливих варіантів:
const pattern = /cat|dog/;
console.log(pattern.test("I have a cat")); // true
console.log(pattern.test("I have a dog")); // true
console.log(pattern.test("I have a bird")); // falseЦей шаблон знаходить або cat, або dog.
Групи дають змогу обмежити область дії альтернативи:
const pattern = /I like (tea|coffee)/;
console.log(pattern.test("I like tea")); // true
console.log(pattern.test("I like coffee")); // true
console.log(pattern.test("I like water")); // falseВираз (tea|coffee) означає: після I like має бути слово tea або coffee.
Без групи шаблон мав би інший зміст:
I like tea|coffeeТут | розділяє весь вираз на дві альтернативи:
I like tea;
coffee.
Тому такий шаблон також знайде coffee у рядку I drink coffee, хоча це може бути неочікувано.
Правильний варіант:
I like (tea|coffee)Альтернатива має низький пріоритет. Тому вираз:
cat|dog+означає:
cat;
do, після якого одна або більше літер g.
Він не означає «одне або більше повторень cat чи dog».
Щоб застосувати квантифікатор до всіх варіантів, використовуйте групу:
(cat|dog)+Приклад:
const pattern = /^(cat|dog)+$/;
console.log(pattern.test("cat")); // true
console.log(pattern.test("dog")); // true
console.log(pattern.test("catdog")); // true
console.log(pattern.test("catdogcat")); // true
console.log(pattern.test("bird")); // falseЯкорі ^ і $ гарантують, що весь рядок складається лише з послідовності cat і dog.
Звичайна група не лише об’єднує частини шаблону, а й запам’ятовує знайдений текст. Такі групи називають захоплювальними.
const pattern = /(\d{4})-(\d{2})-(\d{2})/;
const result = "Дата: 2026-07-26".match(pattern);
console.log(result);
// [
// "2026-07-26",
// "2026",
// "07",
// "26"
// ]У результаті:
result[0] — увесь збіг;
result[1] — перша група (\d{4});
result[2] — друга група (\d{2});
result[3] — третя група (\d{2}).
Це зручно для вилучення частин рядка:
const datePattern = /^(\d{4})-(\d{2})-(\d{2})$/;
const match = "2026-07-26".match(datePattern);
if (match) {
const [, year, month, day] = match;
console.log(year); // "2026"
console.log(month); // "07"
console.log(day); // "26"
}Порожній елемент після коми пропускає весь збіг match[0].
Захоплювальна група може зберігати один із кількох варіантів:
const pattern = /^(https?):\/\/(.+)$/;
const match = "https://example.com".match(pattern);
if (match) {
console.log(match[1]); // "https"
console.log(match[2]); // "example.com"
}Перша група зберігає протокол — http або https, а друга — решту адреси.
Іноді група потрібна лише для об’єднання частин шаблону, але її значення не потрібно отримувати в результаті. Для цього використовують незахоплювальну групу:
(?:...)Наприклад:
const pattern = /^(?:https?|ftp):\/\/.+$/;
console.log(pattern.test("https://example.com")); // true
console.log(pattern.test("http://example.com")); // true
console.log(pattern.test("ftp://files.example.com")); // trueГрупа (?:https?|ftp) потрібна для альтернативи, але не створює окремого елемента в результаті match.
Порівняйте:
const capturing = /^(https?|ftp):\/\/(.+)$/;
const nonCapturing = /^(?:https?|ftp):\/\/(.+)$/;
console.log("https://example.com".match(capturing));
// ["https://example.com", "https", "example.com"]
console.log("https://example.com".match(nonCapturing));
// ["https://example.com", "example.com"]Незахоплювальні групи корисні, коли:
група потрібна для квантифікатора;
група об’єднує альтернативи;
значення групи не використовується в коді;
потрібно не змінювати нумерацію інших захоплювальних груп.
Якщо шаблон має багато груп, звертатися до них за номерами може бути незручно. В JavaScript можна використовувати іменовані групи:
(?<name>...)Приклад:
const pattern = /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/;
const match = "2026-07-26".match(pattern);
if (match) {
console.log(match.groups.year); // "2026"
console.log(match.groups.month); // "07"
console.log(match.groups.day); // "26"
}Іменовані групи роблять код зрозумілішим, особливо коли частини шаблону мають конкретне призначення.
Зворотне посилання перевіряє, чи повторюється раніше захоплений текст.
Для групи з номером використовують:
\1Наприклад, шаблон для пошуку однакових слів, написаних двічі поспіль:
const pattern = /\b(\w+)\s+\1\b/i;
console.log(pattern.test("hello hello")); // true
console.log(pattern.test("hello world")); // false
console.log(pattern.test("JavaScript JavaScript")); // trueЯк це працює:
(\w+) захоплює слово;
\s+ знаходить один або більше пробілів;
\1 вимагає повторення саме того тексту, який захопила перша група;
прапорець i ігнорує регістр.
Для іменованої групи використовують синтаксис \k<name>:
const pattern = /\b(?<word>\w+)\s+\k<word>\b/i;
console.log(pattern.test("test test")); // true
console.log(pattern.test("test best")); // falseЗворотні посилання корисні, коли потрібно перевірити не просто однаковий формат, а саме однаковий текст.
Розглянемо шаблон для перевірки ідентифікатора користувача. Умови:
дозволені латинські літери, цифри та символ підкреслення;
ідентифікатор має починатися з літери;
довжина — від 3 до 16 символів;
дозволені також два варіанти спеціального префікса: user_ або admin_.
const usernamePattern = /^(?:user_|admin_)?[A-Za-z][A-Za-z0-9_]{2,15}$/;
const usernames = [
"user_alex",
"admin_root",
"alex_2026",
"1user",
"ab",
"admin-user"
];
for (const username of usernames) {
console.log(username, usernamePattern.test(username));
}Розберімо шаблон:
^
(?:user_|admin_)?
[A-Za-z]
[A-Za-z0-9_]{2,15}
$^ — початок рядка;
(?:user_|admin_)? — необов’язковий префікс;
[A-Za-z] — перший символ має бути літерою;
[A-Za-z0-9_]{2,15} — ще від 2 до 15 дозволених символів;
$ — кінець рядка.
Загальна довжина без префікса становить від 3 до 16 символів. Якщо префікс дозволяється додавати додатково, тоді максимальна довжина всього рядка буде більшою. Якщо обмеження 16 символів має стосуватися всього ідентифікатора, шаблон потрібно будувати інакше:
const usernamePattern = /^(?=.{3,16}$)(?:user_|admin_)?[A-Za-z][A-Za-z0-9_]*$/;Тут позитивний випереджальний перегляд (?=.{3,16}$) спочатку перевіряє загальну довжину рядка.
Один шаблон може обробляти кілька форматів даних. Наприклад, дату можна записувати через дефіс або крапку:
const datePattern = /^(?<day>\d{2})(?:[-.])(?<month>\d{2})(?:[-.])(?<year>\d{4})$/;
const dates = [
"26-07-2026",
"26.07.2026",
"26/07/2026"
];
for (const date of dates) {
const match = date.match(datePattern);
if (match) {
console.log(
`${match.groups.day}.${match.groups.month}.${match.groups.year}`
);
} else {
console.log("Непідтримуваний формат");
}
}У цьому прикладі [-.] — символьний клас, який допускає дефіс або крапку.
Однак такий шаблон дозволить змішувати роздільники:
26-07.2026Якщо потрібно, щоб у всій даті використовувався один і той самий роздільник, можна захопити його в групу та повторно перевірити за допомогою зворотного посилання:
const datePattern = /^(?<day>\d{2})(?<separator>[-.])(?<month>\d{2})\k<separator>(?<year>\d{4})$/;
console.log(datePattern.test("26-07-2026")); // true
console.log(datePattern.test("26.07.2026")); // true
console.log(datePattern.test("26-07.2026")); // falseГрупа (?<separator>[-.]) зберігає перший роздільник, а \k<separator> вимагає використати такий самий роздільник вдруге.
replaceГрупи особливо корисні під час заміни тексту. У рядку заміни можна звертатися до захоплених груп через $1, $2 тощо.
const date = "2026-07-26";
const formattedDate = date.replace(
/^(\d{4})-(\d{2})-(\d{2})$/,
"$3.$2.$1"
);
console.log(formattedDate); // "26.07.2026"Для іменованих груп можна використовувати об’єкт у функції заміни:
const date = "2026-07-26";
const formattedDate = date.replace(
/^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/,
(_, groups) => `${groups.day}.${groups.month}.${groups.year}`
);
console.log(formattedDate); // "26.07.2026"Функція заміни отримує:
повний збіг;
значення іменованих груп;
інші службові аргументи.
У цьому прикладі другий параметр groups містить об’єкт із полями year, month і day.
Створімо функцію, яка знаходить електронну адресу та розділяє її на ім’я користувача і домен.
function parseEmail(email) {
const pattern = /^(?<local>[A-Za-z0-9._%+-]+)@(?<domain>[A-Za-z0-9.-]+\.[A-Za-z]{2,})$/;
const match = email.match(pattern);
if (!match) {
return null;
}
return {
local: match.groups.local,
domain: match.groups.domain
};
}
console.log(parseEmail("anna.petrenko@example.com"));
// { local: "anna.petrenko", domain: "example.com" }
console.log(parseEmail("invalid-email"));
// nullШаблон складається з таких частин:
^ і $ обмежують збіг усім рядком;
(?<local>...) захоплює частину до @;
@ є обов’язковим роздільником;
(?<domain>...) захоплює домен;
\. позначає буквальну крапку;
{2,} вимагає щонайменше дві літери в доменній зоні.
Цей приклад перевіряє базовий формат, але не гарантує, що адреса реально існує або що домен доступний.
Неправильно:
^yes|no$Такий вираз відповідає рядку, який починається з yes, або закінчується на no.
Правильно:
^(yes|no)$Тепер весь рядок має бути або yes, або no.
Ще краще, якщо значення не потрібно захоплювати:
^(?:yes|no)$Неправильно:
red|blue?? застосовується лише до останньої літери e.
Якщо потрібно зробити необов’язковим один із кольорів, використовуйте групу:
(red|blue)?Якщо група потрібна лише для логіки шаблону, не обов’язково робити її захоплювальною:
const pattern = /^(?:https?|ftp):\/\/.+$/;Це зменшує кількість непотрібних значень у результаті збігу та робить шаблон зрозумілішим.
У складних виразах легко помилитися з номерами:
const pattern = /(year)-(\d{2})-(month)-(\d{2})/;Якщо шаблон зміниться і в ньому з’явиться нова група, посилання на $1, $2 або \1 можуть почати означати інші частини. Для важливих даних краще використовувати іменовані групи.
Крапка в регулярному виразі означає будь-який символ. Щоб знайти саме крапку, використовуйте \.:
console.log("file.txt".match(/file.txt/)); // Збіг може бути неочікуваним
console.log("file.txt".match(/file\.txt/)); // Пошук буквальної крапкиТакож крапку можна помістити в символьний клас:
[-.]Круглі дужки (...) об’єднують частини регулярного виразу в групу.
Символ | задає альтернативи — один варіант або інший.
Групи потрібні, щоб квантифікатор застосовувався до всього підвиразу.
Звичайні групи захоплюють знайдений текст.
Незахоплювальні групи (?:...) об’єднують частини без збереження результату.
Іменовані групи (?<name>...) роблять доступ до результатів зрозумілішим.
Зворотні посилання \1 і \k<name> перевіряють повторення раніше знайденого тексту.
Альтернативи потрібно групувати, якщо вони мають діяти лише в межах певної частини шаблону.
Якорі ^ і $ допомагають перевіряти весь рядок, а не лише його частину.