Към съдържанието
wannacode

Регулярни изрази

Проблем

Шестнайсет реда за една дата

Формулярът за резервация иска дата във вида дд.мм.гггг. Проверяваш го символ по символ:

function isDate(s) {
if (s.length !== 10) {
return false;
}
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (i === 2 || i === 5) {
if (c !== ".") {
return false;
}
} else if (c < "0" || c > "9") {
return false;
}
}
return true;
}
console.log(isDate("05.03.2026"));
console.log(isDate("5.3.2026"));
console.log(isDate("05/03/2026"));
Изход
true
false
false

Шестнайсет реда, а функцията проверява само формата: две цифри, точка, две цифри, точка, четири цифри. Ако искаш да приемаш и / вместо точка, или ден с една цифра, ще добавиш още толкова. Номер на телефон, имейл или регистрационен номер ще ти струват по още една такава функция.

Идеята

Шаблон вместо код

Описваш как изглежда текстът, а JavaScript проверява:

function isDate(s) {
return /^\d{2}\.\d{2}\.\d{4}$/.test(s);
}
console.log(isDate("05.03.2026"));
console.log(isDate("5.3.2026"));
console.log(isDate("05/03/2026"));
Изход
true
false
false

Шаблонът се чете отляво надясно: \d е цифра, {2} значи „два пъти“, \. е точка. Такъв шаблон се казва регулярен израз (regular expression, regex).

Шаблонът стои между две наклонени черти: /.../. Това е литерал на регулярен израз, както "..." е литерал на текст. test(текст) връща true, ако намери шаблона някъде в текста. Затова шаблонът започва с ^ („началото на текста“) и завършва с $ („краят“): така трябва да покрие целия текст, от първия до последния символ.

Виж го

Как шаблонът търси

replace не проверява целия текст, а търси парчетата, които отговарят на шаблона, и ги заменя. Ето как минава по реда:

const line = "120 € на 05.03.2026, падеж 15.04.2026";
const masked = line.replace(/\d{2}\.\d{2}\.\d{4}/g, "[дата]");
console.log(masked);
Стъпка по стъпка, с думи
  1. Редът е поредица от знаци, всеки със своя позиция.
  2. От позиция 0: „1“ и „2“ са цифри, но после идва „0“ вместо точка. Опитът пада.
  3. Нов опит от всеки следващ знак: „20 “, „0 “, „ €“… все неуспехи.
  4. От позиция 9 всичките десет части пасват: първото съвпадение.
  5. Търсенето продължава след съвпадението, от запетаята, и намира второто.
  6. replace с флаг g заменя двете съвпадения с „[дата]“.

Шаблонът се пробва от всяка позиция поред. Щом някоя част не съвпадне, опитът пада и започва нов от следващия символ. Затова 120 не мина: две цифри, а после 0 вместо точка. Когато намери съвпадение, търсенето продължава след него.

Флагът g (global) след шаблона, /.../g, казва „всички съвпадения“. Без него replace заменя само първото.

Идеята

Класове от символи

В квадратни скоби изброяваш кои символи са позволени на едно място. Регистрационен номер на кола е една или две букви, четири цифри и две букви:

const plates = ["СА1234ВХ", "В7788КК", "СА12345ВХ", "са1234вх"];
for (const plate of plates) {
console.log(plate, /^[А-Я]{1,2}\d{4}[А-Я]{2}$/.test(plate));
}
Изход
СА1234ВХ true
В7788КК true
СА12345ВХ false
са1234вх false
Шаблон Съвпада с един символ
[АВЕ] А, В или Е
[0-9], [А-Я] от интервала: цифра, главна буква на кирилица
[^0-9] всичко освен цифра (^ в началото на скобите)
\d цифра, същото като [0-9]
\s интервал, табулация, нов ред
\w латинска буква, цифра или _
. какъвто и да е символ (без нов ред)

Главните букви обръщат смисъла: \D е „не цифра“, \S „не интервал“, \W „не \w“.

Внимавай с \w: в JavaScript той е само латиница.

console.log(/^\w+$/.test("Sofia"));
console.log(/^\w+$/.test("София"));
console.log(/^\p{L}+$/u.test("София"));
console.log(/^[А-Яа-я]+$/.test("София"));
Изход
true
false
true
true

За текст на кирилица пиши [А-Яа-я] или \p{L} с флага u: \p{L} е буква от всеки език, а u (unicode) казва на шаблона да разбира такива класове.

По-точно, за любопитните
  • [А-Я] е интервал по кодовете на символите. В него влизат и руските Ы и Э, но не и Ё, която стои отделно в таблицата. За българския текст [А-Яа-я] стига. Ако те интересува и „ѝ“, добави го: [А-Яа-яѝЍ].
  • Истинските български номера ползват само 12 букви, които изглеждат еднакво на кирилица и латиница: [АВЕКМНОРСТУХ].

Пробвай

Точката

Някой е забравил обратната черта пред точките:

Какво ще отпечата програмата?

const pattern = /^\d{2}.\d{2}.\d{4}$/;
console.log(pattern.test("05.03.2026"));
console.log(pattern.test("05-03-2026"));
console.log(pattern.test("05.03.26"));
Отговор

Идеята

Колко пъти

След символ или клас казваш колко пъти може да се повтори. Това е квантификатор:

Квантификатор Значи
? нула или едно (може да го има, може да го няма)
* нула или повече
+ едно или повече
{4} точно 4
{1,2} от 1 до 2
{2,} 2 или повече

Мобилен номер започва с 0 или +359, после 87, 88 или 89 и още 7 цифри:

const mobile = /^(\+359|0)8[789]\d{7}$/;
console.log(mobile.test("0888123456"));
console.log(mobile.test("+359877654321"));
console.log(mobile.test("0888 123 456"));
console.log(mobile.test("088812345"));
Изход
true
true
false
false

| значи „или“. Скобите държат \+359|0 заедно: без тях „или“ би разделило целия шаблон на две. Скобите правят още неща, които ще видим в следващия урок. + в началото е със \, защото иначе е квантификатор.

Квантификаторът важи и за цяла група в скоби. Цена с дробна част или без нея:

const price = /^\d+([.,]\d{1,2})? €$/;
console.log(price.test("12 €"));
console.log(price.test("12.5 €"));
console.log(price.test("12,50 €"));
console.log(price.test(".50 €"));
Изход
true
true
true
false

([.,]\d{1,2})? значи: „точка или запетая и една-две цифри“, а целият този блок може да го няма.

Идеята

Литерал или низ

В литерала /.../ обратната черта е само за шаблона: \d е цифра, \. е точка, без удвояване.

Понякога шаблонът се сглобява от текст, например когато част от него идва от променлива. Тогава е низ в кавички за new RegExp(...), и минава през два слоя: първо JavaScript чете низа, после шаблона. В низ \ започва специален символ: \n е нов ред, \" е кавичка. \d не е такъв, и JavaScript просто маха обратната черта, без грешка и без предупреждение:

const date = "05.03.2026";
const pattern = new RegExp("^\d{2}\.\d{2}\.\d{4}$");
console.log(pattern.source);
console.log(pattern.test(date));
Изход
^d{2}.d{2}.d{4}$
false

source показва шаблона, който е получил RegExp: d{2} значи „две букви d“. Затова в низ всяко \ от шаблона се пише \\:

const date = "05.03.2026";
console.log(/^\d{2}\.\d{2}\.\d{4}$/.test(date));
console.log(new RegExp("^\\d{2}\\.\\d{2}\\.\\d{4}$").test(date));
console.log(/^C:\\Temp$/.test("C:\\Temp"));
Изход
true
true
true
Искаш в шаблона Литерал Низ за new RegExp
\d (цифра) /\d/ "\\d"
\. (истинска точка) /\./ "\\."
\\ (истинска обратна черта) /\\/ "\\\\"

В литерала и / е със \: /\//, иначе би затворила шаблона.

Символите, които трябва да са със \, когато ги искаш буквално, са . * + ? ( ) [ ] { } | ^ $ \.

Пробвай

Намери грешката

Програмата трябва да отпечата „12 € и 50 цента“, а печата това:
Изход
0 € и 0 цента

Избери реда с грешката.

const price = "12.50";
console.log(price.split("."));
console.log(price.split(/./));
Изход
[ '12', '50' ]
[ '', '', '', '', '', '' ]

Идеята

Замяна и разделяне по шаблон

replace и split приемат шаблон. Така чистиш и разбиваш текст, който хората пишат както им падне:

const address = "Доставка: ул. Шипка 12, вход Б";
console.log(address.replace(/\s+/g, " "));
const items = "хляб, мляко;кафе чай".split(/[,;\s]+/);
console.log(`${items.length}: ${items.join(" | ")}`);
console.log("Код SUMMER10, код summer25".replace(/summer\d+/gi, "***"));
Изход
Доставка: ул. Шипка 12, вход Б
4: хляб | мляко | кафе | чай
Код ***, код ***
  • \s+ е „един или повече интервала“: всяка група интервали става един.
  • [,;\s]+ е „запетаи, точки и запетаи или интервали, колкото и да са“: разделителят може да е всеки от тях.
  • i след шаблона е флаг: шаблонът не гледа главни и малки букви. Флаговете се пишат един до друг: gi е „всички съвпадения, без значение от главните букви“.

replace с текст вместо шаблон заменя само първата поява. За всички е replaceAll("стар", "нов"). Ако не ти трябва шаблон, ползвай текст: не трябва да мислиш за точки и обратни черти.

\b е граница на дума: мястото между буква и нещо, което не е буква. С него заменяш само цели думи. Но в JavaScript \b, като \w, познава само латиница, дори с флаг u:

const text = "стар и старо";
console.log(text.replace(/стар/g, "нов"));
console.log(text.replace(/\bстар\b/g, "нов"));
console.log(text.replace(/(?<!\p{L})стар(?!\p{L})/gu, "нов"));
Изход
нов и ново
стар и старо
нов и старо

Без \b „старо“ става „ново“. С \b JavaScript не вижда кирилицата като букви и не заменя нищо. За кирилица границата се пише с поглед (lookaround): (?<!\p{L}) е „отпред няма буква“, а (?!\p{L}) е „отзад няма буква“. Те не взимат символи, само проверяват.

По-точно, за любопитните
  • \B е обратното на \b: място вътре в дума. И той познава само латиница, затова на кирилица намира „стар“ и в „старо“. Само в средата на дума е (?<=\p{L})стар с флаг u: „отпред има буква“.
  • ^ е началото на текста, а $ краят: text.replace(/^0+/, "") маха водещите нули.
  • Флаговете са букви след шаблона: g (всички), i (без значение от главните букви), u (unicode), m и s (в следващия урок). (?i) в началото на шаблона в Node 22 е грешка.
  • "a|b".split(/|/) е капан като точката: | е „или“ и шаблонът е празен, затова дели между всеки два символа. "a|b".split("|") търси обикновен текст и няма този проблем.
console.log("застаря старо".match(/\Bстар/g));
console.log("застаря старо".match(/(?<=\p{L})стар/gu));
console.log("a|b".split(/|/));
console.log("a|b".split("|"));
console.log("a a".replace("a", "b"), "a a".replaceAll("a", "b"));
Изход
[ 'стар', 'стар' ]
[ 'стар' ]
[ 'a', '|', 'b' ]
[ 'a', 'b' ]
b a b b

Пробвай

Пощенски код

Българският пощенски код е точно четири цифри. Какво липсва, за да отпечата програмата true за "1000" и false за "100" или "10000"?

const code = "1000";
console.log(/^$/.test(code));

Упражнение

Проверки с шаблон

Реши задачите в редактора си. Чети редове, докато има, така (засега го копирай):

import { createInterface } from "node:readline";
const rl = createInterface({ input: process.stdin });
for await (const line of rl) {
// line е поредният ред, без "\n"
}

В урока „Файлове“ ще четеш същите редове от файл.

1. Дата. Провери всяка дата дали е във вида дд/мм/гггг. Денят е от 01 до 31, а месецът от 01 до 12. Подсказка: денят е 0[1-9] или [12]\d, или 3[01]. В литерала / се пише \/.

Вход
01/01/2020
32/01/2020
15/05/2021
Изход
01/01/2020 - Валидна
32/01/2020 - Невалидна
15/05/2021 - Валидна

2. Мобилен номер. Хората пишат номера с интервали и тирета. Махни ги с replace и провери това, което остава, с шаблона за мобилен номер от урока.

Вход
0888 123 456
+359 87 765 4321
02 812 3456
0899-12-345
Изход
0888 123 456 - валиден
+359 87 765 4321 - валиден
02 812 3456 - невалиден
0899-12-345 - невалиден

3. Имейл. Провери дали всеки ред прилича на имейл: една или повече латински букви, цифри, ., + или -, после @, после име на домейн с поне една точка (firma.bg, mail.firma.bg).

Вход
elena.koleva@firma.bg
elena@firma
@firma.bg
elena koleva@firma.bg
Изход
elena.koleva@firma.bg - да
elena@firma - не
@firma.bg - не
elena koleva@firma.bg - не

4. Списък за пазаруване. Продуктите са разделени със запетаи, точки и запетаи, вертикални черти или интервали, понякога по няколко наведнъж. Отпечатай ги номерирани.

Вход
хляб, мляко;кафе чай|сирене
Изход
1. хляб
2. мляко
3. кафе
4. чай
5. сирене
По-точно, за любопитните
  • Шаблонът за дата приема и 31/02/2026. Дали денят съществува в месеца, regex не може да каже разумно. Шаблонът е само първото сито. Вграденият Date тук не помага: той не отказва 31 февруари, а го прехвърля в 3 март (месеците в Date се броят от 0, затова февруари е 1):
console.log(new Date(2026, 1, 31).toDateString());
Изход
Tue Mar 03 2026

Затова провери деня сам: построй датата и виж дали месецът е останал същият.

  • Пълното правило за имейл адрес (RFC 5322) позволява кавички, коментари и интервали в определени места. Шаблон, който го покрива изцяло, е стотици символи и никой не го поддържа. Истинската проверка е да изпратиш писмо с връзка за потвърждение.

Още задачи

  • Парола. Провери дали паролата е поне 8 символа и има поне една цифра. Направи го с два test: /^.{8,}$/ и /\d/. Защо на втория не ти трябват ^ и $?
  • Час. Провери час във вида чч:мм от 00:00 до 23:59.
  • Само цифри. От "Тел.: +359 (88) 812-34-56" остави само цифрите и + с един replace и [^...].

Тест

Провери се

1. Какво дава /\d{4}/.test("12026")?
2. С кое съвпада . (точка) в шаблон?
3. Кое значи „едно или повече“?
4. Съвпада ли "София" с /^\w+$/ в JavaScript?
5. Колко части дава "хляб, мляко;кафе".split(/[,;]\s*/)?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·