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.
Znak | Znaczenie |
---|---|
. (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.
1 |
Pattern p = Pattern.compile("Wyrażenia regularne"); |
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().
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import java.util.regex.Matcher; import java.util.regex.Pattern; public class Test { public static void main(String[] args) { Pattern compiledPattern = Pattern.compile("Ala"); Matcher matcher = compiledPattern.matcher("Ala ma kota"); System.out.println("metoda find zwraca: " + matcher.find()); System.out.println("metoda matches zwraca: " +matcher.matches()); } } |
Przykład 2
Poniższy listing pokazuje użycie metod replaceAll()
oraz replaceFirst()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import java.util.regex.Matcher; import java.util.regex.Pattern; public class Test { public static void main(String[] args) { Pattern p = Pattern.compile("Java"); String stringToReplace = "Programista Java, Matcher Java, replaceFirst Java, replaceAll Java"; Matcher m = p.matcher(stringToReplace); System.out.println("łańcuch do zamiany: " + stringToReplace); System.out.println("Przykład użycia metody replaceFirst(): " + m.replaceFirst("jAVA")); System.out.println("Przykład użycia metody replaceAll(): " + m.replaceAll("jAVA")); } } |
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+)*