Регулярний вираз у JavaScript — це об'єкт, що описує текстовий шаблон. Створюється за допомогою літерала `/pattern/flags` або `new RegExp('pattern', 'flags')`.
**Три найпоширеніші методи**
```javascript
const text = 'User: alice, age 30';
/\d+/.test(text) // true - чи є патерн десь у рядку?
text.match(/\d+/) // ['30'] - перший збіг як масив (або null)
text.replace(/\d+/, '??') // 'User: alice, age ??' - замінити перший збіг
text.replace(/\d+/g, '??')// 'User: alice, age ??' - замінити ВСІ (прапор g)
```
**Читаємо патерн посимвольно**
Візьмемо `/([\w.]+)@([\w.]+\.\w{2,})/` (простий патерн email):
```
( відкрити групу 1
[\w.]+ один або більше: символи слова або літеральна крапка
) закрити групу 1
@ літеральний "@"
( відкрити групу 2
[\w.]+ один або більше: символи слова або літеральна крапка
\. літеральна крапка (екранована - звичайна . означає "будь-який символ")
\w{2,} 2 або більше символів слова (домен верхнього рівня)
) закрити групу 2
```
```javascript
const m = '[email protected]'.match(/([\w.]+)@([\w.]+\.\w{2,})/);
m[0] // '[email protected]' - повний збіг
m[1] // 'bob' - група 1
m[2] // 'example.com' - група 2
```
**Літерал regex vs new RegExp**
Перевагу надавай літералу `/pattern/` — він компілюється один раз і не потребує екранування рядків. Використовуй `new RegExp(str)` лише коли патерн будується під час виконання:
```javascript
// Добре: літерал
const valid = /^\d{3}-\d{4}$/;
// Коли патерн з змінної:
function buildSearch(word) {
const escaped = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return new RegExp(escaped, 'gi');
}
```
**Пастка `lastIndex` зі збереженням стану**
Коли використовуєш прапор `g` або `y` і викликаєш `.test()` або `.exec()` на *тому самому* об'єкті regex кілька разів, рушій запам'ятовує позицію через `regex.lastIndex`. Це призводить до дивного чергування true/false:
```javascript
const re = /\d+/g; // зі збереженням стану через 'g'
re.test('abc 42 def'); // true (знайшов '42', lastIndex -> 6)
re.test('abc 42 def'); // false (продовжує з індексу 6, нічого не знаходить)
re.test('abc 42 def'); // true (lastIndex скидається після невдачі)
// Безпечний патерн: створюй новий regex щоразу або скидай вручну
re.lastIndex = 0;
re.test('abc 42 def'); // true (завжди)
```
Найбезпечніше правило: використовуй прапор `g` лише з `str.match()`, `str.matchAll()` або `str.replaceAll()`, а не з повторними викликами `.test()` на тому самому об'єкті regex.
**Захоплюючі групи** дозволяють витягувати частини збігу. Результат `.match()` без прапора `g` містить повний збіг за індексом 0, потім кожну групу.
```javascript
const log = '2024-03-15 ERROR disk full';
const m = log.match(/(\d{4}-\d{2}-\d{2}) (\w+) (.+)/);
m[1] // '2024-03-15'
m[2] // 'ERROR'
m[3] // 'disk full'
```
**Іменовані групи** `(?<name>...)` — доступ через `m.groups.name`:
```javascript
const m = log.match(/(?<date>\d{4}-\d{2}-\d{2}) (?<level>\w+) (?<msg>.+)/);
m.groups.date // '2024-03-15'
m.groups.level // 'ERROR'
m.groups.msg // 'disk full'
```
Іменовані групи також доступні в `replace` через `$<name>`:
```javascript
'2024-03-15'.replace(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/, '$<d>/$<m>/$<y>')
// '15/03/2024'
```
**replace з функцією**
Коли заміна є функцією, вона отримує `(fullMatch, ...groups, offset, originalStr)`:
```javascript
'hello world'.replace(/(\w+)/g, (match) => match.toUpperCase())
// 'HELLO WORLD'
'a=3, b=7'.replace(/(\w+)=(\d+)/g, (_, key, val) => `${key}=${+val * 2}`)
// 'a=6, b=14'
```
**matchAll — ітерація по всіх збігах з групами**
`.match()` з прапором `g` повертає лише повні збіги (групи відкидаються). Використовуй `str.matchAll(regex)`, щоб отримати всі збіги *разом* з їхніми групами:
```javascript
const text = 'alice=30, bob=25';
const re = /(\w+)=(\d+)/g;
for (const m of text.matchAll(re)) {
console.log(m[1], m[2]); // 'alice' '30' потім 'bob' '25'
}
// або зібрати результати:
const pairs = [...text.matchAll(re)].map(m => ({ name: m[1], age: +m[2] }));
// [{ name: 'alice', age: 30 }, { name: 'bob', age: 25 }]
```
`matchAll` вимагає прапор `g` і повертає ітератор — розгортай або ітеруй з `for...of`.
**Не-захоплюючі групи** `(?:...)` групують без індексу захоплення — зручні для чергування:
```javascript
// Знайти 'colour' або 'color'
'colour and color'.match(/colou?r/g) // ['colour', 'color']
// Чергування без захоплюючої групи
'cats and dogs'.match(/(?:cat|dog)s?/g) // ['cats', 'dogs']
```
Lookahead, lookbehind, жадібні/ліниві та динамічний regex
**Lookahead** перевіряє, що йде *після* поточної позиції, не поглинаючи символів.
```javascript
// Позитивний lookahead (?=...) - збіг X, лише якщо після нього Y
'100px 200em 300px'.match(/\d+(?=px)/g) // ['100', '300'] (числа перед 'px')
// Негативний lookahead (?!...) - збіг X, лише якщо після нього НЕ Y
'100px 200em'.match(/\d+(?!px)/g) // ['200'] (числа не перед 'px')
```
**Lookbehind** (ES2018) перевіряє, що йде *перед*:
```javascript
// Позитивний lookbehind (?<=...) - збіг X, лише якщо перед ним Y
'$100 EUR200'.match(/(?<=\$)\d+/g) // ['100'] (числа після '$')
// Негативний lookbehind (?<!...) - збіг X, лише якщо перед ним НЕ Y
'$100 EUR200'.match(/(?<!\$)\d+/g) // ['200'] (числа не після '$')
```
**Жадібні vs ліниві**
Квантифікатори за замовчуванням жадібні. Додай `?`, щоб зробити їх лінивими:
```javascript
const html = '<b>bold</b> and <i>italic</i>';
html.match(/<.+>/g) // ['<b>bold</b> and <i>italic</i>'] жадібний
html.match(/<.+?>/g) // ['<b>', '</b>', '<i>', '</i>'] лінивий
```
**Екранування введення користувача для динамічного regex**
Якщо будуєш regex з рядка, введеного користувачем, спочатку обов'язково екрануй всі спеціальні символи regex — інакше крапка або зірочка зламають патерн:
```javascript
function escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function highlight(text, query) {
const re = new RegExp(escapeRegex(query), 'gi');
return text.replace(re, '<mark>$&</mark>');
}
highlight('Ціна: 2.50', '2.50') // 'Ціна: <mark>2.50</mark>'
// без екранування '.' збіглася б з будь-яким символом
```
**Типові пастки**
- Забути прапор `g`: `'aaa'.replace(/a/, 'b')` дає `'baa'`, а не `'bbb'`
- `.` не збігається з новими рядками за замовчуванням — використовуй `[\s\S]` або прапор `s`
- `^` і `$` збігаються з межами рядка, а не рядка тексту — якщо не використовується прапор `m`
- `.test()` у циклі з тим самим `g` regex викликає зміщення `lastIndex` (див. Блок 1)
- Regex не може парсити HTML — використовуй DOM
Готова колекція патернів для поширених задач у JavaScript.
**Email (простий)**
```javascript
const EMAIL = /[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}/g;
'[email protected], [email protected]'.match(EMAIL)
// ['[email protected]', '[email protected]']
```
**ISO дата**
```javascript
const ISO = /\b(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\b/g;
'Захід 2024-03-15, дедлайн 2024-12-31'.match(ISO)
// ['2024-03-15', '2024-12-31']
```
**URL**
```javascript
const URL_RE = /https?:\/\/[^\s<>'"]+/g;
'Дивись https://developer.mozilla.org для деталей'.match(URL_RE)
// ['https://developer.mozilla.org']
```
**Slug (нижній регістр з дефісами)**
```javascript
function toSlug(str) {
return str.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
}
toSlug('Hello World! 42') // 'hello-world-42'
```
**Camel до snake**
```javascript
function camelToSnake(str) {
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
}
camelToSnake('myVariableName') // 'my_variable_name'
```
**Маска кредитної карти (показати останні 4)**
```javascript
'4111 1111 1111 1234'.replace(/\d(?=\d{4})/g, '*')
// '**** **** **** 1234'
```
**Довідник прапорів**
```
g global - знайти всі збіги (не лише перший)
i без урахування регістру
m multiline - ^ та $ збігаються з межами рядків
s dotAll - . збігається і з новими рядками
u unicode - вмикає \u{...} та класи Unicode
d hasIndices - match.indices[n] дає [start, end] для кожної групи
```
**Коли НЕ використовувати regex у JavaScript**
- Парсинг HTML - використовуй `document.querySelector`, `DOMParser` або бібліотеку
- Парсинг URL - використовуй `new URL(str)`
- Парсинг JSON - використовуй `JSON.parse`
- Сувора валідація email - regex не може забезпечити RFC 5322; використовуй бібліотеку
Regex блискуче підходить для сканування та перетворення звичайного тексту. Для структурованих форматів завжди надавай перевагу спеціалізованому парсеру.
Витягніть усі слова з рядка, що починаються з великої літери. Поверніть їх як масив.
function capitalWords(str) {
// ваш код тут
}
console.log(capitalWords('Hello world, My Name is Alice')); // ['Hello', 'My', 'Name', 'Alice']
console.log(capitalWords('no capitals here')); // []
Рішення
function capitalWords(str) {
return str.match(/\b[A-Z][a-zA-Z]*/g) || [];
}
Підрахуйте, скільки разів слово зустрічається в рядку (без врахування регістру, тільки цілі слова).
function countWord(str, word) {
// ваш код тут
}
console.log(countWord('The cat sat on the mat. The cat is fat.', 'the')); // 3
console.log(countWord('cats and cat and cat', 'cat')); // 3 (not 'cats')
Рішення
function countWord(str, word) {
const escaped = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const re = new RegExp(`\\b${escaped}\\b`, 'gi');
return (str.match(re) || []).length;
}
Витягніть усі URL з рядка. URL починається з http:// або https:// і закінчується пробільним символом або кінцем рядка.
function extractUrls(str) {
// ваш код тут
}
const text = 'Visit https://example.com and http://foo.org/path?q=1 for details.';
console.log(extractUrls(text));
// ['https://example.com', 'http://foo.org/path?q=1']
Рішення
function extractUrls(str) {
return str.match(/https?:\/\/[^\s]+/g) || [];
}
No split tab
Налаштування cookies
Ми використовуємо необхідні cookies для роботи сайту. З вашого дозволу ми також можемо зберігати налаштування сайту та використовувати аналітичні й рекламні cookies, щоб розуміти використання сайту й підтримувати розвиток проєкту.
* Ви завжди можете змінити свій вибір у налаштуваннях сайту.
Оберіть категорії cookies
Налаштування аналітики
Можна вимкнути аналітику використання платформи. Також можна надіслати в Google Analytics запит на видалення даних про використання цього сайту, пов'язаних із цим браузером.
Навчальний workspace
Навчайтеся, читаючи, запускаючи код і розв'язуючи задачі.
Практикуйте програмування з поясненнями тем, вправами, інструментами browser IDE, перевіркою regex і тренуванням друку коду в одному workspace.
Відкривайте інструменти у вкладках.Вправи, IDE-інструменти й тренажери залишаються доступними як вкладки сайту.
Перемикайтеся без втрати контексту.Переходьте між поясненнями, кодом та утилітами, зберігаючи своє місце.
Використовуйте sidebar як карту.Ліві панелі містять навігацію, налаштування, файли, libraries та керування інструментами.
PythonJavaScriptSQLite
Одна IDE, три практичні режими
Python у браузері.Запускайте невеликі скрипти, пробуйте бібліотеки й тренуйте API-запити без встановлення.
JavaScript для швидких експериментів.Перевіряйте код для браузера й порівнюйте ідеї поруч із навчальними матеріалами.
SQLite для практики з даними.Відкривайте оглядач бази даних, переглядайте таблиці, пишіть запити й вивчайте SQL локально.
ТемаIDE
Працюйте поруч у split tabs
Тримайте інструкції перед очима.Відкрийте вправу або довідкову сторінку поруч з IDE, замість постійних перемикань.
Порівнюйте інструменти під час навчання.Розміщуйте перевірки regex, пояснення та експерименти з кодом поруч, коли це потрібно для задачі.
Закрийте split, коли завершите.Workspace повернеться до однієї сфокусованої вкладки, а відкриті вкладки сайту залишаться доступними.
Тренажер друку коду
Або звичайного тексту
Тренажер розрахований на фізичну клавіатуру.Відкрийте цей розділ на ноутбуці або комп'ютері з широким екраном. На телефоні тренування друку не працюватиме коректно.
Швидкість: 0 зн/хв
0 слів/хв
Найкраща швидкість (60с): 0 зн/хв
0 слів/хв
Помилки: 0
Загальний час: 0.0 с
Щоб тренувати сліпий друк, не підглядайте на фізичну клавіатуру.