Wyrażenia regularne, powszechnie znane jako regex lub regexp, to jedno z najmocniejszych narzędzi do pracy z tekstem dostępnych programistom i analitykom danych.
- Fundamenty wyrażeń regularnych – zrozumienie podstawowych koncepcji
- Podstawowa składnia – znaki, klasy znaków i escapowanie
- Kwantyfikatory i powtórzenia – kontrola liczby dopasowań
- Grupy, przechwytywanie i odwołania zwrotne
- Kotwice i granice – określanie pozycji w tekście
- Lookahead i lookbehind – zaawansowane asercje pozycji
- Flagi i modyfikatory – zmienianie zachowania wyrażeń
- Praktyczne przykłady – wyrażenia regularne w rzeczywistych scenariuszach
- Metody i funkcje – używanie wyrażeń regularnych w kodzie
- Optymalizacja wydajności – pisanie efektywnych wyrażeń regularnych
- Narzędzia i zasoby – nauka i testowanie wyrażeń regularnych
- Zaawansowane koncepty – rekurencja, warunkowość i złożone wzorce
- Praktyczne wdrażanie – integracja regex w projektach
- Wytyczne najlepszych praktyk
W największym skrócie: wyrażenie regularne to wzorzec opisujący tekst do wyszukania, walidacji lub transformacji. Oprócz prostego wyszukiwania umożliwia ono walidację danych, ekstrakcję informacji i przekształcanie łańcuchów znaków.
Idea regex sięga lat 50. XX wieku (Stephen Cole Kleene), a od 1968 r. trafiła do praktyki inżynierskiej (edytory tekstu, kompilatory). Obecnie regex jest standardem w większości języków programowania, m.in. w Java, Python, JavaScript i C#.
Fundamenty wyrażeń regularnych – zrozumienie podstawowych koncepcji
Wyrażenie regularne to zbiór reguł, które ciąg znaków musi spełnić, aby „pasować” do wzorca. Z punktu widzenia teorii języków formalnych opisuje ono (często nieskończony) zbiór dopuszczalnych ciągów.
Najczęściej spotykane silniki regex wykorzystują backtracking: próbują dopasować wzorzec od każdej pozycji w tekście, cofając się przy niepowodzeniach. Zbyt ogólne lub nieprecyzyjne wzorce mogą istotnie pogorszyć wydajność, zwłaszcza na dużych danych.
Regex jest czytany z lewej do prawej; każdy element wzorca ma znaczenie zależne od kontekstu znaków sąsiadujących.
Podstawowa składnia – znaki, klasy znaków i escapowanie
Aby sprawnie pisać i czytać regex, warto znać najpopularniejsze skrócone klasy znaków:
- \d – dowolna cyfra (równoważne
[0-9]); - \w – litera, cyfra lub podkreślenie;
- \s – biały znak (spacja, tabulator, nowa linia);
- \D, \W, \S – zaprzeczenia powyższych klas.
Kropka . dopasowuje dowolny pojedynczy znak (poza nową linią, chyba że włączysz flagę s). Używaj jej oszczędnie, bo prowadzi do nieoczekiwanych dopasowań. Aby dopasować dosłowną kropkę, użyj \. lub [.].
Escapowanie znaków specjalnych jest kluczowe: ., *, +, ?, (, ), [, ], {, }, \, ^, $ należy poprzedzić backslashem (np. \., \(). W wielu silnikach sekwencja \Q … \E traktuje wszystko w środku jako tekst literalny.
Klasy znaków [] dopasowują jeden znak z podanego zbioru, np. [abc], zakresy [a-z], łączenia [a-zA-Z0-9] i negację [^abc]. Pamiętaj, że niektóre metaznaki wewnątrz [] zmieniają znaczenie (np. ^ neguje tylko na początku klasy).
Kwantyfikatory i powtórzenia – kontrola liczby dopasowań
Najważniejsze kwantyfikatory i ich zastosowania to:
- * – zero lub więcej powtórzeń (np.
a*); - + – jedno lub więcej powtórzeń (np.
a+); - ? – zero lub jedno wystąpienie (np.
colou?r); - {n}, {min,max}, {min,} – precyzyjne sterowanie liczbą powtórzeń (np.
\d{2,4}).
Domyślnie kwantyfikatory są chciwe (greedy). Aby były leniwe (lazy), dodaj ? po kwantyfikatorze: *?, +?, ??, {min,max}?. Np. .*?apple zatrzyma się na pierwszym „apple”.
Uważaj na catastrophic backtracking, np. w (a+)+b na długich łańcuchach „a”. Zapobiegaj mu przez precyzyjniejsze wzorce, grupy atomowe (?>…) i kwantyfikatory posesywne (np. a++).
Grupy, przechwytywanie i odwołania zwrotne
Poniżej zebrano typy grup i ich zastosowania:
- grupy przechwytujące (…) – umożliwiają stosowanie kwantyfikatorów do fragmentu i przechwytywanie dopasowań (odwołania
\1,\2); - grupy nieprzechwytujące (?:…) – porządkują wzorzec bez tworzenia odwołań zwrotnych (lepsza wydajność i czytelność);
- nazwane grupy (?<nazwa>…) – przechwytywanie z czytelnymi odwołaniami po nazwie (np.
(?<rok>\d{4})).
Przykład wykrywania powtórzonych słów: \b(\w+)\b\s+\1\b.
Kotwice i granice – określanie pozycji w tekście
Najczęstsze asercje pozycji to:
- ^ – początek tekstu lub linii (z flagą m);
- $ – koniec tekstu lub linii (z flagą m);
- \b – granica słowa (między znakami „słowo” i „nie-słowo”);
- \B – brak granicy słowa (pozycja wewnątrz słowa).
Przykład: ^hello$ dopasuje dokładnie „hello”, a z flagą m – całe linie zawierające „hello”.
Lookahead i lookbehind – zaawansowane asercje pozycji
Lookaround pozwala sprawdzać kontekst przed/za pozycją bez konsumowania znaków:
- pozytywny lookahead (?=…) – dopasuj, jeśli dalej występuje wzorzec (np.
\d+(?=\s\w+)); - negatywny lookahead (?!…) – dopasuj, jeśli dalej nie występuje wzorzec (np.
\d+(?!\d)); - pozytywny lookbehind (?<=…) – dopasuj, jeśli wcześniej wystąpił wzorzec (np.
(?<=\$)\d+); - negatywny lookbehind (?<!…) – dopasuj, jeśli wcześniej nie wystąpił wzorzec (np.
(?<!\d)\d{3}(?!\d)).
Ekstrakcja treści linku bez tagów może wyglądać tak: (?<=<a[^>]*>)(.*?)(?=</a>) (wymaga lookbehind). Alternatywnie użyj przechwytywania: <a[^>]*>(.*?)</a>.
Flagi i modyfikatory – zmienianie zachowania wyrażeń
Najpopularniejsze flagi i ich działanie:
- i – ignoruje wielkość liter (np.
/Hello/i); - g – tryb globalny: zwraca wszystkie dopasowania (np.
str.match(/\d+/g)); - m – wielolinijkowość: zmienia znaczenie
^i$na granice linii; - s – dotall: kropka
.dopasowuje także nową linię; - x – tryb „verbose”: pozwala na białe znaki i komentarze w wyrażeniu.
Praktyczne przykłady – wyrażenia regularne w rzeczywistych scenariuszach
Poniżej znajdziesz użyteczne wzorce wraz z krótkim omówieniem:
- prosta walidacja e-maila –
^[\w.-]+@[\w-]+\.[a-z]{2,24}$(części: lokalna,@, domena, kropka, sufiks); - telefon z opcjonalnym kodem kraju –
^\+?\d{1,3}[-.\s]?\d{3}[-.\s]?\d{3}[-.\s]?\d{3,4}$(wersja restrykcyjna:^\+\d{1,3}\d{9}$); - ekstrakcja ceny „$99.99” –
\$(\d+\.\d{2})(wartość w grupie1); - weryfikacja hasła –
^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}$(lookaheady sprawdzają warunki, końcówka wymusza długość).
Metody i funkcje – używanie wyrażeń regularnych w kodzie
Poniższa tabela zestawia podstawowe operacje w popularnych językach:
| Język | Szukanie (pierwsze) | Wszystkie dopasowania | Zamiana | Dzielenie | Uwagi |
|---|---|---|---|---|---|
| Python | re.search() |
re.findall() |
re.sub() |
re.split() |
moduł re, używaj surowych łańcuchów r''. |
| JavaScript | RegExp.test() / RegExp.exec() |
String.match() z g / String.matchAll() |
String.replace() / replaceAll() |
String.split() |
flagi w /wzorzec/gimsu, obiekt RegExp. |
| C# | Regex.IsMatch() / Regex.Match() |
Regex.Matches() |
Regex.Replace() |
Regex.Split() |
używaj łańcuchów verbatim @"" dla backslashy. |
Przykładowe czyszczenie danych (Python): re.sub(r'[^a-zA-Z0-9\s]', '', tekst).
Optymalizacja wydajności – pisanie efektywnych wyrażeń regularnych
Te praktyki pomogą zwiększyć szybkość i przewidywalność działania:
- używaj specyficznych klas znaków zamiast kropki (np.
/[a-z0-9-]*error[a-z0-9-]*/izamiast/.*error.*/); - stosuj kotwice ^ i $, aby zawęzić zakres dopasowań i przyspieszyć negatywne przypadki;
- unikaj catastrophic backtracking przez grupy atomowe
(?>…)i kwantyfikatory posesywne++; - prekompiluj wzorce i wielokrotnie je wykorzystuj, by zredukować koszty kompilacji.
Przykład z pomiarów: niezoptymalizowane .*error.* – 4320 ms (92% CPU) vs. zoptymalizowane – 687 ms (18% CPU), ok. 6,3x szybciej.
Narzędzia i zasoby – nauka i testowanie wyrażeń regularnych
Warto sięgnąć po sprawdzone testery i materiały edukacyjne:
- regex101.com – PCRE/Python/JS/Java/C#/Rust, szczegółowe wyjaśnienia dopasowań;
- regexr.com – interaktywny tester z biblioteką przykładów;
- regexlearn.com – kurs krok po kroku dla początkujących;
- regexp.pl – tester zgodny z PHP/PCRE, pomocny przy SQL i backendzie;
- IntelliJ IDEA – wsparcie regex w IDE (np.
Ctrl+Hdo wyszukiwania/zamiany).
Zaawansowane koncepty – rekurencja, warunkowość i złożone wzorce
Poniżej zebrano wybrane techniki dla wymagających wzorców (głównie PCRE):
- rekurencja (?R) – wzorzec odwołujący się do siebie (np. zagnieżdżone klamry
~\{(?R)?\}~); - warunkowość –
(?(warunek)wtedy|inaczej), przydatna dla formatów opcjonalnych; - branch reset groups – współdzielenie numerów grup w alternatywach dla czytelności;
- podwzorce/procedury –
(?(DEFINE)(?<nazwa>wzor))i wywołanie(?&nazwa)dla modularności.
Praktyczne wdrażanie – integracja regex w projektach
Typowe zastosowania w codziennej pracy:
- walidacja w formularzach – np. wymagaj 9 cyfr:
^\d{9}$(Google Forms/Sheets); - web scraping – uzupełnienie parserów (np. BeautifulSoup) o czyszczenie i normalizację tekstu;
- analiza logów i DevOps – wzorce w Logstash/Fluentd, szybkie filtry (np.
^ERROR: .*$); - edytory i IDE – „znajdź i zamień” w wielu plikach (np. konwersja
camelCase→snake_case).
Wytyczne najlepszych praktyk
Poniższe zasady zwiększają czytelność i niezawodność wzorców:
- testuj wzorce – zawsze w warunkach zbliżonych do produkcji i na danych brudnych;
- pisz prosto i precyzyjnie – unikaj nadmiernego użycia
.i zbyt ogólnych klas; - zapobiegaj catastrophic backtracking – stosuj kotwice, grupy atomowe i kwantyfikatory posesywne;
- używaj nazwanych grup – zwiększają czytelność i ułatwiają utrzymanie kodu;
- rozważ alternatywy – dla bardzo dużych plików i złożnych formatów czasem lepszy jest parser lub automat stanów.
