Wyrażenia regularne

Wyrażenia regularne stanowią doskonały sposób na badanie i modyfikowanie tekstu. Dzięki swej olbrzymiej elastyczności pozwalają w łatwy sposób pobierać pasujące fragmenty tekstu. To tyle książkowej teorii.

A czym są wzorce w praktyce? Powiedzmy, że mamy fragment tekstu, w którym znajdują się jakieś kody pocztowe. Kod taki składa się z dwóch cyfr, myślnika, po którym występują trzy cyfry. Jeżeli chcielibyśmy znaleźć w takim tekście wszystkie kody, nie moglibyśmy użyć znanych nam już indexOf() czy includes(), ponieważ nie znamy konkretnej wartości. Znamy wzorzec.

Wyrażenia regularne nie sa domeną Javascriptu. Gdy nauczymy się ich - nawet w podstawowej formie - bardzo ułatwią nam codzienną prace z kodem. Za ich pomocą możemy dla przykładu w każdym porządnym edytorze zamieniać podobne wystąpienia tekstu (np. klasy, daty itp), masowo zmieniać nazwy plików itp. Poniżej przykład takiej zamiany:

Pisanie podstawowych wzorów wyrażeń nie jest jakieś bardzo skomplikowane. Problem pojawia się przy tych bardziej rozbudowanych. Tutaj częstokroć trzeba korzystać z Google szukając frazy "fraza test regexp" i posiłkować się poradami ze Stack Overflow.

Bardzo też przydają się tutaj narzędzia takie jak: https://regex101.com/arrow-up-right, czy http://regexr.com/arrow-up-right.

Na koniec warto też zerknąć na stronkę https://regexlearn.comarrow-up-right, która interaktywnie uczy pisać takie wyrażenia.

Wyrażenia w Javascript

Aby w JavaScript korzystać z wyrażeń regularnych, możemy posłużyć się skróconym zapisem /wzor/ lub użyć konstruktora RegExp(wzór, flagi*), który przyjmuje 2 argumenty: wzór, którym będziemy testować, oraz dodatkowe flagi, które poznamy poniżej.

const reg = /pani?/gi

//lub za pomocą konstruktora

const reg = new RegExp("pani?" , "gi")

Gdy już stworzymy wzór, musimy go użyć wraz z jedną z dostępnych metod. Je omówimy sobie w kolejnym rozdziale. W poniższych przykładach będe korzystał głównie z metody test(), która zwraca prawdę lub fałsz.

const reg = /[0-9]{3}/
console.log(reg.test("Ala")) //false
console.log(reg.test("102")) //true "102"
console.log(reg.test("Ala 007")) //true "Ala 007"
console.log(reg.test("Numer czołgu miał numer 102 bo tak")) //true "Numer czołgu miał numer 102 bo tak"

Flagi czyli dodatkowe opcje dla wyrażeń

Dla każdego wyrażenia regularnego możemy ustawić dodatkowe flagi (opcje), które zmieniają jego działanie:

Przy skróconej składni flagi umieszczamy za wyrażeniem regularnym. Dla obiektu RegExp umieszczamy je jako drugi parametr:

znak Flagi
znaczenie

i

powoduje niebranie pod uwagę wielkości liter

znak Flagi
znaczenie

g

powoduje zwracanie wszystkich pasujących fragmentów, a nie tylko pierwszego

znak Flagi
znaczenie

m

powoduje wyszukiwanie w tekście kilku liniowym. W trybie tym znak początku i końca wzorca (^$) jest wstawiany dla każdej linii z osobna.

znak Flagi
znaczenie

s

Sprawia, że znak . pasuje także do znaku nowe linii (\n)

znak Flagi
znaczenie

u

Włącza możliwość używania kodów dla znaków unicode

znak Flagi
znaczenie

y

Włącza tryb sticky. Kolejne wyszukiwania będą rozpoczynać się od pozycji ostatniego szukania, którą definiuje lastIndex

Pozycja w tekście

Domyślnie szukany fragment może znajdować się w dowolnym miejscu.

^

Znak ^ oznacza, że szukany fragment musi znajdować się na początku badanego tekstu

$

Znak $ oznacza, że szukany fragment musi znajdować się na końcu badanego tekstu

Jeżeli chcemy sprawdzić, czy dany fragment występuje na końcu linii wiele liniowego tekstu, powinniśmy dodać do wyrażenia flagę m

Ilości znaków

*

Znak * oznacza 0 lub więcej wystąpień poprzedzającej grupy lub znaku.

+

Znak + oznacza 1 lub więcej wystąpień poprzedzającego znaku lub grupy.

{}

Wewnątrz klamer podajemy liczbę znaków. Możemy tutaj podać konkretną wartość {4}, minimalną {4,}, maksymalną {,4} lub zakres znaków {2,4}:

?

Znak ? oznacza 0 lub 1 wystąpienie poprzedzającego znaku lub grupy.

Użycie znaku ? za znakami ., + lub {} sprawia, że wyszukiwanie przechodzi w tryb wyszukiwania niezachłannego. Oznacza to, że pasujący ciąg skończy się na najmniejszym pasującym fragmencie, a nie największym

Zbiory znaków

[...]

Między nawiasy kwadratowe możemy wstawić znaki, które będą stanowić zbiór, który weźmie udział w teście

Podając zbiory znaków możemy określać ich zakresy:

Zbiory znaków możemy też negować za pomocą znaku ^:

Możemy też skorzystać z przygotowanych dla nas grup znaków:

Klasa znaków
znaczenie

\d

każdy znak będący cyfrą. Równoznaczne z [0-9]

\D

każdy znak nie będący cyfrą. Równoznaczne z [^0-9]

\w

każdy znak będący literą, cyfrą i znakiem _. Równoznaczne z [a-zA-Z0-9_]

\W

każdy znak nie będący literą, cyfrą i znakiem _. Równoznaczne z [^a-zA-Z0-9_]

\s

znak spacji, tabulacji lub nowego wiersza

\S

każdy znak nie będący spacją, tabulacją lub znakiem nowego wiersza

\n

znak nowego wiersza

\t

znak tabulacji

\uXXXX

oznacza znak o danym kodzie Unicode. Do użycia wymaga odpowiedniej flagi

.

oznacza dowolny znak nie będący znakiem nowej linii. Żeby oznaczał też nową linię musimy użyć odpowiedniej flagi.

Dodatkowo mamy tutaj specjalne oznaczenie \b, które oznacza granicę słowa.

Możemy go użyć w trzech kluczowych miejscach:

  • na początku słowa, od którego zaczynają się znaki \w

  • na końcu słowa przed którym są znaki \w

  • w środku słowa gdzie po obu stronach są znaki \w

Wybór lub

Znak | oznacza lub

Grupy

Okrywając kawałek wzoru nawiasami robimy z niego grupę, do której możemy się później odwoływać.

W powyższych przykładach używaliśmy metody .test(), która zwraca wartość boolean.

Przy pracy z grupami warto skorzystać z dodatkowych metod takich jak .match() lub .exec() czy replace()arrow-up-right, które zwracają tablicę pasujących fragmentów:

Do kolejnych znalezionych członów (grup) możemy potem odwoływać się poprzez zapis $1, $2 itd.

Możemy to wykorzystać w metodzie replace(), ale też podczas pracy w edytorze tekstu.

Dopasowywane w zależności

Zapis ?= pozwala nam dopasowywać dany fragment tekstu, gdy tuż za nim występuje inny fragment tekstu:

W ES2018 mamy też bardzo podobny zapis ?<=, który pozwala nam sprawdzać czy dany ciąg poprzedza jakiś tekst:

Unicode w wyrażeniach regularnych

Domyślnie wyrażenia regularne - tak samo jak reszta tekstów w javascript operuje na pierwszych 1600 znakach z tablicy Unicode. Jeżeli chcielibyśmy działać na niestandardowych znakach (np. Chińskich literach czy ikonach Emoji), powinniśmy do naszego wzoru dodać flagę u:

Last updated