Regex Builder
Regex Builder – opanuj wyrażenia regularne bez bólu głowy
Wyrażenia regularne potrafią być… brutalne. Pełne znaków specjalnych, trudne do czytania i jeszcze trudniejsze do napisania od zera.
Regex Builder powstał po to, żeby to zmienić.
To interaktywna aplikacja do nauki i testowania wyrażeń regularnych w JavaScript, która pozwala budować regexy z klocków, testować je na żywo i rozumieć, co dokładnie robi każdy fragment.
Buduj regex krok po kroku
Nie musisz już pamiętać całej składni regex na pamięć.
Zamiast tego:
- składasz wyrażenie z gotowych elementów (np. „dowolna liczba”, „słowo”, „email”, „grupa”)
- widzisz od razu, jak wygląda wynikowy regex
- możesz w każdej chwili edytować lub rozbić go na części
Regex przestaje być czarną magią — staje się konstrukcją.
Testuj i ucz się na żywo
- natychmiastowe podświetlanie dopasowań w tekście
- lista wszystkich matchy
- obsługa flag (
g,i,m, itd.) - szybka informacja o błędach składni
Każda zmiana w regexie od razu pokazuje efekt.
Explain Regex – zrozum, co naprawdę się dzieje
Każdy fragment wyrażenia:
- jest opisany prostym językiem
- pokazuje swoje znaczenie i zakres działania
- pomaga zrozumieć dlaczego regex działa (lub nie)
Idealne narzędzie do nauki, powtórek i „aha momentów”.
Dla kogo?
- dla początkujących, którzy boją się regexów
- dla programistów, którzy „kopiują z internetu i modlą się, żeby działało”
- dla nauczycieli i studentów
- dla każdego, kto chce rozumieć, a nie tylko używać
Dlaczego warto?
Składnia regex JavaScript
Znaki meta
a|b– Pasuje do a lub b.– Dopasowuje dowolny znak (symbol wieloznaczny) z wyjątkiem znaków kończących wiersz\w– Dopasowuje znaki słowa (alfanumeryczne i _)\W– Dopasowuje znaki niebędące słowami\d– Dopasowuje cyfry (0-9)\D– Dopasowuje znaki niebędące cyframi\s– Dopasowuje znaki odstępu (spacja, tabulator, nowy wiersz)\S– Dopasowuje znak inny niż biały[\b]– Dopasowuje znaki cofania\0– Dopasowuje znaki NULL\n– Dopasowuje znaki nowego wiersza\f– Dopasowuje znaki końca linii\r– Dopasowuje znaki powrotu karetki\t– Dopasowuje znaki tabulacji\v– Dopasowuje znaki tabulacji pionowej\p{}– Dopasowuje znaki o podanej właściwości Unicode\P{}– Dopasowuje znak NIE o podanej właściwości Unicode\ddd– Dopasowuje znak o liczbie ósemkowej ddd\xhh– Dopasowuje znak o liczbie szesnastkowej hh\uhhhh– Dopasowuje znak Unicode o liczbie szesnastkowej hhhh
Klasy znaków
[a]– Dopasowuje znak w nawiasach kwadratowych[^a]– Dopasowuje wszystkie znaki NIE znajdujące się w nawiasach kwadratowych[abc]– Dopasowuje wszystkie znaki w nawiasach kwadratowych[^abc]– Dopasowuje wszystkie znaki NIE znajdujące się w nawiasach kwadratowych[a-z]– Dopasowuje wszystkie znaki z zakresu od a do z[^a-z]– Dopasowuje wszystkie znaki NIE znajdujące się w zakresie od a do z[0-9]– Dopasowuje wszystkie znaki z zakresu od 0 do 9[^0-9]– Dopasowuje wszystkie znaki NIE znajdujące się w zakresie od 0 do 9
Kwantyfikatory
x+– Pasuje do co najmniej jednego xx*– Pasuje do zera lub więcej wystąpień xx?– Pasuje do zera lub jednego wystąpienia xx{n}– Pasuje do n wystąpień xx{n,m}– Pasuje do od n do m wystąpień xx{n,}– Pasuje do n lub więcej wystąpień x
Grupy
(x)– Dopasowuje x i zapamiętuje dopasowanie(?<n>x)– Dopasowuje x i oznacza je etykietą n(?flag:x)– Włącza flagę(y) dla x(?flag-flag:x)– Wyłącza flagę(y) dla x
Granice
^– Dopasowuje początek ciągu znaków lub początek wiersza, jeśli ustawiona jest flaga m (wielowierszowa)$– Dopasowuje koniec ciągu znaków lub koniec wiersza, jeśli ustawiona jest flaga m (wielowierszowa)\b– Dopasowuje początek lub koniec słowa\B– Dopasowuje NIE początek ani koniec słowa(?=...)– Dopasowuje kolejny ciąg znaków(?!...)– Dopasowuje NIE kolejny ciąg znaków(?<=...)– Dopasowuje poprzedni ciąg znaków(?<!...)– Dopasowuje NIE poprzedni ciąg znaków
Flagi
/d– Wykonuje dopasowania podciągów/g– Wykonuje dopasowanie globalne (znajdź wszystko)/i– Wykonuje dopasowanie bez uwzględniania wielkości liter/m– Wykonuje dopasowanie wielowierszowe/s– Umożliwia użycie kropki (.) do dopasowywania terminatorów wiersza/u– Włącza obsługę Unicode/v– Uaktualnienie flagi /u zapewniające lepszą obsługę Unicode/y– Wykonuje wyszukiwanie „lepkie”
Przykłady wyrażeń regularnych z tekstem testowym
- Wyszukiwanie adresów e-mail
Wyrażenie:
/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/
Tekst testowy:
Kontakt: jan.kowalski@example.com lub support@firma123.pl
Niepoprawny: user@domain, mail@.com
- Dopasowanie dat w formacie YYYY-MM-DD
Wyrażenie:
/\b\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\b/
Tekst testowy:
Poprawne daty: 2023-04-15, 2025-12-01
Niepoprawne: 2023-13-01, 2023-02-30
- Wyszukiwanie numerów telefonów (format PL)
Wyrażenie:
/\b\d{3}[- ]?\d{3}[- ]?\d{3}\b/
Tekst testowy:
Kontakt: 123-456-789 lub 987 654 321
Niepoprawny: 12345678, 12-345-6789
- Dopasowanie kodów pocztowych (PL)
Wyrażenie:
/\b\d{2}-\d{3}\b/
Tekst testowy:
Poprawne kody: 00-123, 87-654
Niepoprawne: 00123, 87 654
- Wyszukiwanie adresów URL
Wyrażenie:
/https?:\/\/(www\.)?[a-z0-9.-]+\.[a-z]{2,}(\/\S*)?/
Tekst testowy:
Odwiedź: https://example.com, http://www.firma.pl/test
Niepoprawne: htp://bad.url, example.com
- Dopasowanie słów zaczynających się od dużej litery
Wyrażenie:
/\b[A-Z][a-z]*\b/
Tekst testowy:
Anna Kowalska mieszka w Warszawie, a jej przyjaciel to janek. Klasy znaków
Specjalne
Kwantyfikatory
Grupy
Granice
Zestawy
Kod po stronie przeglądarki
<link type="text/css" href="http://www.dariuszrorat.ugu.pl/assets/css/bootstrap/wcag-outline.min.css" rel="stylesheet">
<style>
.max-vh-75 {
max-height: 75vh !important;
}
.editor { font-family: monospace; white-space: pre-wrap; min-height: 200px; }
.highlight { background-color: var(--bs-success-bg-subtle) !important; color: var(--bs-success-text-emphasis) !important; border-radius: 4px; padding: 0 2px; }
.error { color: #dc3545; font-weight: bold; }
.token {
display: block;
padding: 4px 6px;
border-left: 4px solid #0d6efd;
background: var(--bs-body-bg-alt);
margin-bottom: 4px;
font-family: monospace;
}
.token.group {
border-left-color: #6f42c1;
background: var(--bs-body-bg-alt);
}
.token.quantifier {
border-left-color: #20c997;
}
.token.literal {
border-left-color: #adb5bd;
}
.token-line {
display: flex;
gap: 10px;
}
.indent {
border-left: 2px dashed #ced4da;
padding-left: 12px;
margin-left: 6px;
}
</style>
<div id="app">
<!-- REGEX INPUT -->
<div class="card mb-3">
<div class="card-header"><i class="bi bi-regex"></i> Wyrażenie regularne</div>
<div class="card-body">
<div class="row g-2 align-items-center mb-2">
<div class="col-auto fs-4">/</div>
<div class="col">
<input id="pattern" class="form-control" value="\w+" aria-label="Wyrażenie regularne">
</div>
<div class="col-auto fs-4">/</div>
<div class="col-2">
<input id="flags" class="form-control" value="g" aria-label="Flagi">
</div>
</div>
<div id="error" class="alert alert-danger mb-0 d-none"></div>
</div>
</div>
<!-- REGEX BUILDER -->
<div class="card mb-3">
<div class="card-header"><i class="bi bi-bricks"></i> Regex Builder</div>
<div class="card-body">
<!-- Klasy znaków -->
<h3 class="h6">Klasy znaków</h3>
<div class="mb-2">
<button class="btn btn-outline-primary btn-sm" onclick="add('\\d')">Cyfra \d</button>
<button class="btn btn-outline-primary btn-sm" onclick="add('\\D')">Nie-cyfra \D</button>
<button class="btn btn-outline-primary btn-sm" onclick="add('\\w')">Znak \w</button>
<button class="btn btn-outline-primary btn-sm" onclick="add('\\W')">Nie-\w</button>
<button class="btn btn-outline-primary btn-sm" onclick="add('\\s')">Spacja \s</button>
<button class="btn btn-outline-primary btn-sm" onclick="add('\\S')">Nie-\s</button>
</div>
<!-- Specjalne -->
<h3 class="h6">Specjalne</h3>
<div class="mb-2">
<button class="btn btn-outline-secondary btn-sm" onclick="add('.')">Dowolny .</button>
<button class="btn btn-outline-secondary btn-sm" onclick="add('\\')">Znak \</button>
</div>
<!-- Kwantyfikatory -->
<h3 class="h6">Kwantyfikatory</h3>
<div class="mb-2">
<button class="btn btn-outline-auto-darklight btn-sm" onclick="add('+')">+</button>
<button class="btn btn-outline-auto-darklight btn-sm" onclick="add('*')">*</button>
<button class="btn btn-outline-auto-darklight btn-sm" onclick="add('?')">?</button>
<button class="btn btn-outline-auto-darklight btn-sm" onclick="wrap('{','}')">{n}</button>
<button class="btn btn-outline-auto-darklight btn-sm" onclick="wrap('{',',}')">{n,m}</button>
</div>
<!-- Grupy -->
<h3 class="h6">Grupy</h3>
<div class="mb-2">
<button class="btn btn-outline-success btn-sm" onclick="wrap('(',')')">( grupa )</button>
<button class="btn btn-outline-success btn-sm" onclick="wrap('(?:',')')">(?: grupa)</button>
<button class="btn btn-outline-success btn-sm" onclick="add('|')">| OR</button>
</div>
<!-- Granice -->
<h3 class="h6">Granice</h3>
<div class="mb-2">
<button class="btn btn-outline-danger btn-sm" onclick="add('^')">^ start</button>
<button class="btn btn-outline-danger btn-sm" onclick="add('$')">$ koniec</button>
<button class="btn btn-outline-danger btn-sm" onclick="add('\\b')">\b granica</button>
<button class="btn btn-outline-danger btn-sm" onclick="add('\\B')">\B granica</button>
</div>
<!-- Zestawy -->
<h3 class="h6">Zestawy</h3>
<div>
<button class="btn btn-outline-info btn-sm" onclick="wrap('[',']')">[abc]</button>
<button class="btn btn-outline-info btn-sm" onclick="wrap('[^',']')">[^abc]</button>
<button class="btn btn-outline-info btn-sm" onclick="wrap('[a-z]', '')">[a-z]</button>
<button class="btn btn-outline-info btn-sm" onclick="wrap('[0-9]', '')">[0-9]</button>
</div>
</div>
</div>
<!-- EXPLAIN -->
<div class="card mb-3">
<div class="card-header"><i class="bi bi-lightbulb"></i> Explain regex</div>
<div class="card-body max-vh-75 overflow-auto" id="explain"></div>
</div>
<!-- Text input -->
<div class="row">
<div class="col-md-6 mb-3">
<div class="card h-100">
<div class="card-header"><i class="bi bi-input-cursor-text"></i> Tekst wejściowy</div>
<div class="card-body">
<textarea id="inputText" class="form-control editor" rows="10" aria-label="Tekst wejściowy">
Test email: test@example.com
URL: https://example.com
Liczby: 123 456
</textarea>
</div>
</div>
</div>
<!-- Highlighted output -->
<div class="col-md-6 mb-3">
<div class="card h-100">
<div class="card-header"><i class="bi bi-highlighter"></i> Podświetlone dopasowania</div>
<div class="card-body editor" id="output"></div>
</div>
</div>
</div>
<!-- Matches -->
<div class="card">
<div class="card-header"><i class="bi bi-search"></i> Dopasowania</div>
<div class="card-body match-list max-vh-75 overflow-auto" id="matches"></div>
</div>
</div>
<script src="http://www.dariuszrorat.ugu.pl/assets/js/highlightjs/languages/regex.js" defer></script>
<script>
const patternInput = document.getElementById("pattern");
const pattern = document.getElementById("pattern");
const flagsInput = document.getElementById("flags");
const textInput = document.getElementById("inputText");
const output = document.getElementById("output");
const errorBox = document.getElementById("error");
const matchesBox = document.getElementById("matches");
const explainBox = document.getElementById("explain");
function add(val) {
const input = pattern;
const start = input.selectionStart;
const end = input.selectionEnd;
const before = input.value.substring(0, start);
const after = input.value.substring(end);
input.value = before + val + after;
// ustaw kursor za wstawioną wartością
const newPos = start + val.length;
input.setSelectionRange(newPos, newPos);
input.focus();
update();
}
function wrap(l, r) {
const input = pattern;
const start = input.selectionStart;
const end = input.selectionEnd;
const before = input.value.substring(0, start);
const selected = input.value.substring(start, end);
const after = input.value.substring(end);
input.value = before + l + selected + r + after;
// jeśli był zaznaczony tekst → zaznacz go z powrotem
if (selected.length > 0) {
input.setSelectionRange(start + l.length, start + l.length + selected.length);
} else {
// jeśli nie było zaznaczenia → kursor do środka
const pos = start + l.length;
input.setSelectionRange(pos, pos);
}
input.focus();
update();
}
function escapeHTML(str) {
return str.replace(/[&<>]/g, c => ({
'&': '&',
'<': '<',
'>': '>'
} [c]));
}
function indentStyle(level) {
return level > 0
? `style="margin-left:${level * 20}px"`
: "";
}
function explainRegex(src) {
const map = {
// === Klasy znaków ===
"\\d": {
label: "Cyfra",
description: "Dowolna cyfra od 0 do 9",
example: "\\d → 5"
},
"\\D": {
label: "Nie-cyfra",
description: "Dowolny znak, który NIE jest cyfrą",
example: "\\D → a"
},
"\\w": {
label: "Znak słowa",
description: "Litera, cyfra lub znak podkreślenia (_)",
example: "\\w → A, 7, _"
},
"\\W": {
label: "Nie-znak słowa",
description: "Dowolny znak poza literą, cyfrą i _",
example: "\\W → @"
},
"\\s": {
label: "Biały znak",
description: "Spacja, tabulator lub nowa linia",
example: "\\s → ' '"
},
"\\S": {
label: "Nie-biały znak",
description: "Dowolny znak poza białymi znakami",
example: "\\S → a"
},
// === Metaznaki ===
".": {
label: "Dowolny znak",
description: "Dowolny pojedynczy znak (poza nową linią)",
example: ". → a"
},
// === Kwantyfikatory ===
"+": {
label: "Jeden lub więcej",
description: "Poprzedni element występuje co najmniej raz",
example: "a+ → a, aaa"
},
"*": {
label: "Zero lub więcej",
description: "Poprzedni element może wystąpić dowolną liczbę razy",
example: "a* → '', aaa"
},
"?": {
label: "Opcjonalny",
description: "Poprzedni element występuje 0 lub 1 raz",
example: "a? → '', a"
},
"{n}": {
label: "Dokładnie n razy",
description: "Poprzedni element musi wystąpić dokładnie n razy",
example: "a{3} → aaa"
},
"{n,}": {
label: "Co najmniej n razy",
description: "Poprzedni element występuje n lub więcej razy",
example: "a{2,} → aa, aaa"
},
"{n,m}": {
label: "Zakres powtórzeń",
description: "Poprzedni element występuje od n do m razy",
example: "a{2,4} → aa, aaa"
},
// === Grupy i alternatywy ===
"(": {
label: "Początek grupy",
description: "Rozpoczyna grupę przechwytującą",
example: "(abc)"
},
")": {
label: "Koniec grupy",
description: "Zamyka grupę przechwytującą",
example: "(abc)"
},
"(?:": {
label: "Grupa nieprzechwytująca",
description: "Grupa używana tylko do logiki, bez zapamiętywania",
example: "(?:abc)"
},
"|": {
label: "Alternatywa (OR)",
description: "Dopasuj lewą LUB prawą stronę",
example: "a|b"
},
// === Kotwice ===
"^": {
label: "Początek tekstu",
description: "Dopasowanie musi zacząć się na początku tekstu",
example: "^abc"
},
"$": {
label: "Koniec tekstu",
description: "Dopasowanie musi zakończyć się na końcu tekstu",
example: "abc$"
},
"\\b": {
label: "Granica słowa",
description: "Początek lub koniec słowa",
example: "\\bword\\b"
},
// === Klasy znaków [] ===
"[abc]": {
label: "Zestaw znaków",
description: "Jeden z podanych znaków",
example: "[abc] → a"
},
"[^abc]": {
label: "Negacja zestawu",
description: "Dowolny znak poza podanymi",
example: "[^abc] → d"
},
// === Znaki specjalne ===
"\\.": {
label: "Kropka",
description: "Dosłowna kropka",
example: "\\. → ."
}
};
let out = "";
let groupLevel = 0;
for (let i = 0; i < src.length; i++) {
let token = src[i];
// Escaped token
if (src[i] === "\\" && src[i + 1]) {
token = src.slice(i, i + 2);
i++;
}
// === Początek grupy ===
if (token === "(") {
groupLevel++;
out += `
<div class="token group" ${indentStyle(groupLevel - 1)}>
<strong>(</strong>
<div class="text-muted">
Początek grupy <strong>#${groupLevel}</strong>
</div>
</div>`;
continue;
}
// === Koniec grupy ===
if (token === ")") {
out += `
<div class="token group" ${indentStyle(groupLevel - 1)}>
<strong>)</strong>
<div class="text-muted">
Koniec grupy <strong>#${groupLevel}</strong>
</div>
</div>`;
groupLevel--;
continue;
}
// === Zwykłe tokeny ===
const info = map[token];
const type =
["+", "*", "?", "{", "}"].includes(token)
? "quantifier"
: "literal";
out += `
<div class="token ${type}" ${indentStyle(groupLevel)}>
<div class="token-line">
<strong>${escapeHTML(token)}</strong>
<span class="text-muted">
${info ? info.label : "Znak literalny"}
</span>
</div>
${info ? `<div>${info.description}</div>` : ""}
${info?.example ? `<div class="small fst-italic">Przykład: ${info.example}</div>` : ""}
</div>`;
}
explainBox.innerHTML = out || '<div class="alert alert-warning mb-0">Brak wzorca</div>';
}
function renderMatch(match, index) {
const start = match.index;
const end = match.index + match[0].length;
let groupsHTML = "";
if (match.length > 1) {
groupsHTML = `<div class="mt-1"><strong>Grupy:</strong><ul class="mb-0">`;
for (let i = 1; i < match.length; i++) {
groupsHTML += `
<li>
<code>(${i})</code>
${match[i] !== undefined ? `"${escapeHTML(match[i])}"` : "<em>brak</em>"}
</li>`;
}
groupsHTML += `</ul></div>`;
} else {
groupsHTML = `<div class="mt-1 text-muted"><em>Brak grup przechwytujących</em></div>`;
}
return `
<div class="card mb-2">
<div class="card-body py-2">
<div class="fw-bold">Match #${index + 1}</div>
<hr class="my-2">
<div><strong>Wartość:</strong> "<code>${escapeHTML(match[0])}</code>"</div>
<div><strong>Pozycja:</strong> ${start}–${end}</div>
${groupsHTML}
</div>
</div>
`;
}
function update() {
errorBox.textContent = "";
matchesBox.innerHTML = "";
output.innerHTML = "";
explainRegex(pattern.value);
if (!errorBox.classList.contains('d-none')) {
errorBox.classList.add('d-none');
}
let regex;
try {
regex = new RegExp(patternInput.value, flagsInput.value);
} catch (e) {
errorBox.textContent = e.message;
errorBox.classList.remove('d-none');
return;
}
const text = textInput.value;
let lastIndex = 0;
let resultHTML = "";
let match;
let index = 0;
while ((match = regex.exec(text)) !== null) {
// ⛑️ ZABEZPIECZENIE PRZED PUSTYM MATCH
if (match[0] === "") {
regex.lastIndex++;
if (regex.lastIndex > text.length) break;
continue;
}
resultHTML += escapeHTML(text.slice(lastIndex, match.index));
resultHTML += `<span class="highlight">${escapeHTML(match[0])}</span>`;
matchesBox.innerHTML += renderMatch(match, index);
index++;
lastIndex = match.index + match[0].length;
if (!regex.global) break;
}
resultHTML += escapeHTML(text.slice(lastIndex));
output.innerHTML = resultHTML || escapeHTML(text);
matchesBox.innerHTML = matchesBox.innerHTML || '<div class="alert alert-warning mb-0">Brak dopasowań</div>';
}
function debounce(func, delay) {
let timer;
return function(...args) {
const context = this;
clearTimeout(timer);
timer = setTimeout(() => func.apply(context, args), delay);
};
}
pattern.oninput = flags.oninput = inputText.oninput = debounce(update, 200);
update();
</script>
Kod po stronie serwera
Brak kodu serwera
Ta aplikacja działa wyłącznie w przeglądarce i nie korzysta z kodu po stronie serwera.
Licencja
## BSD-3-Clause License Agreement
BSD-3-Clause
Сopyright (c) 2026 Dariusz Rorat
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.