Wyrażenia regularne - okładka

Wyrażenia regularne w JavaScript

Opublikowano Kategorie Backend, Frontend, JavaScriptCzas czytania 7min

Na samym początku warto odpowiedzieć na pytanie, czym są wyrażenia regularne? 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 choćby z najzwyklejszego stringa.

String.prototype.search

Metoda search sprawdza, na którym miejscu naszego stringa znajduje się dany znak lub 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 -1.


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 wzorzec w tablicy. W przeciwnym razie zostanie zwrócony null.


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 jest 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 również stworzyć, 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ę 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.


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 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. Poniżej znajdziesz 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 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 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, a 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 = '[email protected]';

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.

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 Regex101, czyli platformy do debuggowania wyrażeń regularnych. Jak zwykle zachęcam też do pozostawiania komentarzy i zadawania pytań.

Źródła i materiały dodatkowe

Dominik Szczepaniak

Zawodowo Senior Software Engineer w CKSource. Prywatnie bloger, fan włoskiej kuchni, miłośnik jazdy na rowerze i treningu siłowego.

Inne wpisy, które mogą Cię zainteresować

Zapisz się na mailing i odbierz e-booka

Odbierz darmowy egzemplarz e-booka 106 Pytań Rekrutacyjnych Junior JavaScript Developer i realnie zwiększ swoje szanse na rozmowie rekrutacyjnej! Będziesz też otrzymywać wartościowe treści i powiadomienia o nowych wpisach na skrzynkę e-mail.

Subscribe
Notify of
guest

2 komentarzy
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Jakub 'unknow' Mrugalski

Sekcje „String.prototype.search()” oraz „String.prototype.match()” mają dokładnie ten sam kod źródłowy w przykładach. Warto poprawić 🙂

Dominik Szczepaniak
5 years ago

Dzięki za czujne oko, już poprawione 😉