Wyrażenia regularne - okładka

Wyrażenia regularne w JavaScript

Na samym początku należy odpowiedzieć na pytanie: Czym są wyrażenia regularne?

Otóż, wyrażenie regularne jest swego rodzaju wzorcem, który umożliwia sprawdzenie, czy dana wartość zgadza się ze zdefiniowanym wzorcem. Wyrażenia regularne nazywane są inaczej RegExpami lub regexami – nazwy te pochodzą z języka angielskiego od Regular Expression. W tym artykule oprócz samych wyrażeń omówię też metody służące do szukania pewnych wzorców w tekstach, gdyż są to tematy powiązane.

Sposoby pracy z wyrażeniami regularnymi i metody na szukanie wzorców

Przed omówieniem samych wyrażeń regularnych omówię kilka metod, których możemy użyć niekoniecznie z samymi wyrażeniami regularnymi. Metody opisane poniżej możemy zastosować korzystając chociażby z najzwyklejszego stringa.

String.prototype.search()

Metoda search() sprawdza na którym miejscu naszego stringa znajduje się dany znak, bądź też ciąg znaków. W przypadku znalezienia pożądanego wzorca metoda zwraca nam liczbę która jest indeksem pierwszego znaku z szukanego wzorca. Jeżeli warunek nie zostanie spełniony metoda zwróci nam -1. Poniżej zostawiam przykład:


const str = "Devszczepaniak.pl to super blog!";
console.log(str.search(".pl"));
console.log(str.search(".com"));

String.prototype.match()

Metoda match() działa bardzo podobnie do metody search. Różnicą między metodami jest zwracana wartość. W przypadku znalezienia wzorca w tekście dostaniemy nasz wzorzec w tablicy. I innym razie zostanie zwrócony null. Tu również pozostawiam przykład:


const str = "Devszczepaniak.pl to super blog!";
console.log(str.match(".pl"));
console.log(str.match(".com"));

Wyrażenia regularne - test

Tworzenie wyrażenia regularnego

Sposobów na stworzenie wyrażenia mamy kilka. Pierwszy z nich to utworzenie nowej instancji obiektu RegExp. Pierwszym parametrem obiektu jest nasz wzorzec, natomiast drugim parametrem są flagi jakie możemy użyć. Regexa możemy stworzyć też po prostu przypisując go do zmiennej. Podczas tworzenia wyrażenia tym sposobem należy pamiętać o otwierającym i kończącym slashu.


const regexObj = new RegExp("Devszczepaniak.pl?")
const regex = /Devszczepaniak.pl?/
const str = "Devszczepaniak.pl to super blog!";

console.log(regexObj.test(str));
console.log(regex.test(str));

console.log(regexObj.test("Hello World!"));
console.log(regex.test("Hello World!"));

Pojawiła nam się tutaj nowa metoda – test(). Ta metoda służy nam do sprawdzania, czy dana wartość odpowiada naszemu regexowi. Wynikiem działania tej metody jest odpowiednio true oraz false.

Szersze możliwości daje nam metoda exec(). Tu oprócz sprawdzenia, czy dany wzorzec występuje możemy się dowiedzieć gdzie i ile razy wystąpił oraz jaki ciąg znaków został sprawdzony. W przypadku gdy dany wzór się nie pojawi zostaje zwrócony null.  Poniżej przykład:


const regex = new RegExp("Devszczepaniak.pl?", "g")
const str = "Devszczepaniak.pl to super blog! Devszczepaniak.pl zawiera wiele ciekawych artykułów!";
let arr = [];
let result = 0;

while(regex.exec(str) !== null) result++

console.log(result);

console.log(regex.exec("Hello World!"));

Jak można zauważyć w naszym wyrażeniu pojawił się drugi parametr, czyli flaga. Flagi wpływają na interpretację naszego wyrażenia. Na przykład flaga g użyta w powyższym kodzie sprawia, że zostaną znalezione wszystkie pasujące ciągi znaków, a nie tylko pierwszy. Innym przykładem flagi może być flaga i, która sprawia, że wielkość liter przestaje mieć znaczenie.

Znaki specjalne oraz reguły

Sama koncepcja wyrażeń regularnych nie miałaby większego sensu, gdyby ograniczała się tylko do tego, co przedstawiłem w powyższych przykładach. Kwintesencją wyrażeń regularnych jest definiowanie ich za pomocą znaków specjalnych oraz reguł.

Znaki specjalne reguły szczegółowo definiują nam warunki, jakie musi spełnić nasza testowana wartość. Stosując odpowiednie reguły możemy sprawdzić tak naprawdę dowolny warunek jaki sobie wymyślimy. Samych reguł jest naprawdę dużo. Uważam, że opisywanie ich tu po kolei mija się z celem.  Po pierwsze są już stworzone gotowe (i bardzo dobre) opracowania wraz z przykładami. Po drugie prawie nikt po przeczytaniu tego artykułu nie zapamiętałby ich wszystkich, a co dopiero ich przeznaczenie. Poznawanie i nauka tworzenia wyrażeń regularnych powinna odbywać się poprzez ich praktyczne stosowanie, dlatego też na końcu wpisu znajdziesz kilka zadań do samodzielnego zrobienia. Przedstawiam dwa, moim zdaniem, bardzo dobre źródła z opisami znaków specjalnych oraz reguł wraz z przykładami:

Praktyczne przykłady zastosowania wyrażeń regularnych

Kod pocztowy


const regex = new RegExp('^[0-9]{2}-[0-9]{3}$');
str = '00-001';

console.log(regex.test(str));

Na samym początku naszego wyrażenia podaliśmy w nawiasach kwadratowych zakres znaków jaki nas interesuje – jest to zdefiniowanie zasięgu. Następnie w nawiasach pokrzywionych podajemy ile znaków z naszego zakresu ma się pojawić. Myślnik traktujemy dosłownie – właśnie jego tam oczekujemy. Następnie nasz początkowy wzorzec się powtarza, zostaje zmieniona tylko liczba wystąpień.

Oprócz tego zauważyć można znaki na początku oraz na końcu naszego wyrażenia. Pierwszy z nich (^) mówi nam o tym, że nasza przeszukiwana wartość ma się rozpoczynać wzorcem, zaś znak końcowy ($) mówi nam, że przeszukiwana wartość ma się kończyć wzorcem. Tym prostym zabiegiem zabezpieczyliśmy się przed dopisaniem czegokolwiek przed lub po naszym kodzie pocztowym.

Adres e-mail


const regex = new RegExp('^[a-zA-Z0-9_.-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,3}');
str = 'kontakt@devszczepaniak.pl';

console.log(regex.test(str));

W tym wyrażeniu nowym elementem jest backslash z kropką. Oznacza on wystąpienie kropki we wzorcu. Należy skorzystać z takiego zabiegu ponieważ kropka w wyrażeniu regularnym oznacza “dowolny znak”. Oprócz tego pojawił się także plus, który oznacza dowlną ilość znaków z danego zasięgu.

Jeśli spróbujesz testować to wyrażenie, to zobaczysz że nie działa ono całkowicie poprawnie. Do poprawnej validacji adresu email zostało stworzone wyrażenie które zostało opisane w standardzie RFC 5322. Znacznie więcej o tym, dlaczego powinniśmy korzystać właśnie z tego standardu przeczytasz tutaj: https://www.regular-expressions.info/email.html.

Przy okazji validacji adresu email należy poruszyć również kwestię bezpieczeństwa. Niektóre z wartości w naszych aplikacjach nie powinny być validowane poprzez samodzielnie napisane wyrażenia. Niedopracowany regex może sprawić, że w naszej aplikacji pojawią się podatności takie jak SQL Injection czy XSS. W takim wypadku znacznie łatwiej i bezpieczniej jest użyć gotowych bibliotek, standardów i rozwiązań.

ID z bazy MongoDB


const regex = new RegExp('^[0-9a-fA-F]{24}$');
str = '507f191e810c19729de860ea';

console.log(regex.test(str));

Jako ostatni przykład pokażę przykład wyrażenia, którego sam użyłem do sprawdzania poprawności wprowadzanego ID do bazy MongoDB. W tej bazie identyfikatorami są 24-znakowe heksadecymalne ciągi.

Podsumowanie

Jak już pewnie zauważyłeś/aś tworzenie wyrażeń regularnych tylko pozornie wydaje się skomplikowane. Oczywiście są i takie, od których może rozboleć głowa, natomiast większość z nich jest relatywnie prosta – zarówno w tworzeniu jak i w zrozumieniu.

Jako zadanie domowe zachęcam Cię do stworzenia następujących wyrażeń regularnych:

  • Wyrażenie sprawdzające poprawność wpisanego numeru konta bankowego,
  • Wyrażenie sprawdzające numer telefonu komórkowego (zwróć uwagę na to, że numer może zostać podany z plusem oraz numerem kierunkowym),
  • Zoptymalizowania wyrażenia regularnego z przykładu z adresem email (podpowiedź – należy skorzystać z flagi).

Jeśli szukasz platformy online do ćwiczenia z wyrażeniami regularnymi to możesz skorzystać z https://regex101.com.
Jak zwykle zachęcam też do pozostawiania komentarzy i zadawania pytań.

Źródła i materiały dodatkowe: