Wyrażenie regularne w Javie (regex)

Wyrażenia regularne to narzędzie służące do odnajdywania ciągów znaków na podstawie zapisanego wzorca. Wielu użytkowników Internetu nawet nie jest świadoma, że miała z nimi do czynienia. Typowym zastosowaniem takich wyrażeń jest walidacja pobieranych danych od użytkownika. Jednakże możemy wykorzystać je także do wydzielenia mniejszych fragmentów z całego łańcucha jak np. wyłuskanie cyfr w przykładzie obliczania wyznacznika macierzy w serwlecie.

Zastosowania wyrażeń regularnych w praktyce

Wyobraźmy sobie przykładową sytuację, w której zamawiamy jedzenie zdalnie i musimy podać dane dostawy. Oprócz adresu przydatne są także adres email oraz numer telefonu. Po wpisaniu błędnych danych możemy uzyskać natychmiastową odpowiedź – zły format telefonu lub błędny adres email. Skąd dostawca pizzy wie o naszym błędzie? Otóż wpisane dane kontaktowe nie pasują do podanego wcześniej uniwersalnego wzorca.

W przypadku adresu email musimy założyć dowolną nazwę użytkownika, po której musi znaleźć się znaczek @, a potem występuje nazwa domeny, na której znajduje się skrzynka pocztowa.

Czym są wyrażenia regularne?

Tak jak pokazaliśmy w przykładzie powyżej są wzorcem, do którego dopasowujemy ciąg znaków. Budowanie własnych wyrażeń regularnych może być trudnym zadaniem, gdyż czasami trudno jest przewidzieć wszystkie pasujące możliwości. Zanim opiszemy przykładowe wyrażenia musimy poznać symbole, które służą do ich budowy.

Każdy dowolny znak zapisujemy po prostu używając go. Jeśli szukamy wyrażenia „Java” nasze wyrażenie będzie wyglądać dokładnie tak samo. Zwróćmy uwagę, że powyższy przykład nie zadziała już przy wyszukiwaniu ciągu „java” (wszystkie małe litery).

Znaki specjalne

Służą do zastępowania nimi ciągu znaków.

ZnakZnaczenie
. (znak kropki)zastępuje dowolną literę (wielką i małą)
*wskazuje, że poprzedni znak lub wyrażenie występuje 0 lub więcej razy.
?wskazuje, że poprzedni znak lub wyrażenie występuje 0 lub 1 raz.
+wskazuje, że poprzedni znak lub wyrażenie występuje 1 lub więcej razy.
{x}powtórzenie x razy.
{x,y}powtórzenie od x do y razy.
^początek łańcucha.
$koniec łańcucha.
|operator lub – działa jak funktor logiczny or.
[0-5]zakres znaków obejmujący zbiór {0,1,2,3,4,5}.

Aby wykorzystać któryś z powyższych znaków, zamiast użyć symbolu zastępczego należy go poprzedzić znakiem \.

Klasa Pattern

Obiekty typu Pattern reprezentuje skompilowany łańcuch znakowy wyrażenia regularnego. Przyjrzyjmy się poniższemu przykładowi.

Do obiektu p przypisana jest wartość zwrócona przez statyczną metodę compile(). Jej argumentem jest obiekt typu String.

Metody

compile(String regex) – Kompiluje podany ciąg znaków do postaci wzorca.

compile(String regex, int flags) – Kompiluje podany ciąg znaków do postaci wzorca korzystając z dodatkowej flagi.

matcher(CharSequence input) – tworzy dopasowanie na podstawie podanego wzorca.

matches(String regex, CharSequence input) – Kompiluje podany ciąg znaków do postaci wzorca i próbuje go dopasować.

Klasa Matcher

Klasa implementująca wyszukiwanie wzorca w łańcuchu znakowym. Obiekty klasy Matcher tworzy się metodą matcher() znajdującą się w klasie Pattern. Po utworzeniu obiektu, przy jego pomocy możemy wykonywać operacje:

  • wyszukiwania wzorca w podanym łańcuchu,
  • dopasowania do wzorca w podanym łańcuchu,
  • zastępowania elementów łańcucha pasujących do wzorca elementów,
  • dopisywania do łańcucha pasujących do wzorca elementów.

Metody

boolean find() – zwraca true, jeśli podany wzorzec znajduje się w łańcuchu.

Boolean find(int index) – zwraca true, jeśli podany wzorzec znajduje się w łańcuchu, poszukiwanie zaczyna od indeksu.

String replaceFirst(String replace) – Zamienia pierwsze wystąpienie ciągu w łańcuchu.

String replaceAll(String replace)– Zamienia wszystkie wystąpienie ciągu w łańcuchu.

Boolean matches() – zwraca true, jeśli podany wzorzec pasuje do całego ciągu.

Przykład 1

Użycie metod find() oraz matches().

Przykład 2

Poniższy listing pokazuje użycie metod replaceAll() oraz replaceFirst().

Przykłady użycia wyrażeń regularnych

Znajdowanie kodu pocztowego

Standardowy kod pocztowy można zapisać w formacie xx-xxx. Z naszego punktu widzenia będą to dwie cyfry oddzielone minusem od 3 kolejnych. Poniżej pokażemy kilka możliwości zapisania wyrażeń, które spełnią powyższe warunki.

  • \d\d-\d\d\d
  • \d{2}-\d{3}
  • [0-9]{2}-[0-9]{3}

Znajdowanie liczb całkowitych

Każda liczba całkowita składa się z cyfr, a w przypadku liczb ujemnych dodatkowo są poprzedzone znakiem -. Często jednak w przypadku dużych liczb dla ułatwienia czytania możemy zastosować grupowanie ich po 3 cyfry oddzielając je znakiem spacji. Poniżej znajduje się przykład takiego wyrażenia.

  • [-|+]\d+((\s)\d+)*

Znajdowanie plików

Plik podzielimy sobie na część zawierającą jego nazwę, kropkę, a na końcu jego rozszerzenie. Plik może zaczynać się od dowolnego znaku nie będącego spacją. Dodatkowo wewnątrz nie mogą znajdować się znaki „< > ?| ” * \ /”, ale wewnątrz nazwy mogą znajdować się kropki. Poniżej znajduje się przykładowe wyrażenie.

  • \w+(.\w+)*