Przedstawiamy Państwu tłumaczenie krótkiego przewodnika po wyrażeniach regularnych w Javie, napisanego przez Jeffa Friesena dla serwisu
javaworld . Dla ułatwienia czytania artykuł podzieliliśmy na kilka części.
Wyrażenia regularne w Javie, część 1
Łączenie wielu zakresów |
Możesz połączyć wiele zakresów w jedną klasę znaków, umieszczając je obok siebie. Na przykład klasa [a-zA-Z] dopasowuje wszystkie znaki alfabetu łacińskiego pisane małymi lub dużymi literami. |
Łączenie wielu zakresów
Możesz połączyć wiele zakresów w jedną klasę znaków, umieszczając je obok siebie. Na przykład klasa
[a-zA-Z]
dopasowuje wszystkie znaki alfabetu łacińskiego pisane małymi lub dużymi literami.
Łączenie klas postaci
Unia klas znaków składa się z kilku zagnieżdżonych klas znaków i dopasowuje wszystkie znaki w powstałej unii. Na przykład klasa
[a-d[m-p]]
dopasowuje znaki od
a
do
d
i od
m
do
p
. Rozważmy następujący przykład: Ten
java RegexDemo [ab[c-e]] abcdef
przykład znajdzie znaki
a
,
b
, i , dla których istnieją dopasowania w :
c
d
e
abcdef
regex = [ab[c-e]]
input = abcdef
Found [a] starting at 0 and ending at 0
Found [b] starting at 1 and ending at 1
Found [c] starting at 2 and ending at 2
Found [d] starting at 3 and ending at 3
Found [e] starting at 4 and ending at 4
Przecięcie klas znaków
Przecięcie klas znaków składa się ze znaków wspólnych dla wszystkich klas zagnieżdżonych i pasuje tylko do znaków wspólnych. Na przykład klasa
[a-z&&[d-f]]
dopasowuje znaki
d
i
e
.
f
Rozważmy następujący przykład:
java RegexDemo "[aeiouy&&[y]]" party
Należy pamiętać, że w moim systemie operacyjnym Windows wymagane są podwójne cudzysłowy, ponieważ powłoka poleceń traktuje je
&
jako separator poleceń. W tym przykładzie zostanie znaleziony tylko znak
y
, który ma dopasowanie w
party
:
regex = [aeiouy&&[y]]
input = party
Found [y] starting at 4 and ending at 4
Odejmowanie klas znaków
Odejmowanie klas znaków obejmuje wszystkie znaki z wyjątkiem tych zawartych w zagnieżdżonych klasach znaków i dopasowuje tylko pozostałe znaki. Na przykład klasa
[a-z&&[^m-p]]
dopasowuje znaki od
a
do
l
i od
q
do
z
:
java RegexDemo "[a-f&&[^a-c]&&[^e]]" abcdefg
W tym przykładzie zostaną znalezione znaki
d
i
f
dla których istnieją dopasowania w
abcdefg
:
regex = [a-f&&[^a-c]&&[^e]]
input = abcdefg
Found [d] starting at 3 and ending at 3
Found [f] starting at 5 and ending at 5
Predefiniowane klasy znaków
Niektóre klasy znaków pojawiają się w
wyrażeniach regularnych na tyle często , że uzasadnia to użycie notacji skróconej. Klasa
Pattern
oferuje predefiniowane klasy znaków jako takie skróty. Można ich używać do upraszczania wyrażeń regularnych i minimalizowania błędów składniowych. Istnieje kilka kategorii predefiniowanych klas znaków:
java.lang.Character
właściwości standardowe, POSIX i Unicode, takie jak skrypt, blok, kategoria i plik binarny. Poniższa lista przedstawia jedynie kategorię klas standardowych:
\d
: Numer. Równoważne [0-9]
.
\D
: Znak nienumeryczny. Równoważne [^0-9]
.
\s
: Znak odstępu. Równoważne [ \t\n\x0B\f\r]
.
\S
: Nie jest to znak odstępu. Równoważne [^\s]
.
\w
: Symbol słowotwórczy. Równoważne [a-zA-Z_0-9]
.
\W
: Nie jest postacią słowotwórczą. Równoważne [^\w]
.
W poniższym przykładzie użyto predefiniowanej klasy znaków
\w
do opisania wszystkich znaków wyrazów w tekście wejściowym:
java RegexDemo \w "aZ.8 _"
Przyjrzyj się uważnie następującym wynikom wykonania, które pokazują, że kropki i spacje nie są uznawane za znaki słowne:
regex = \w
input = aZ.8 _
Found [a] starting at 0 and ending at 0
Found [Z] starting at 1 and ending at 1
Found [8] starting at 3 and ending at 3
Found [_] starting at 5 and ending at 5
Separatory linii |
Dokumentacja zestawu SDK klasy Pattern opisuje metaznak kropki jako predefiniowaną klasę znaków, która pasuje do dowolnego znaku z wyjątkiem separatorów linii (sekwencje jedno- lub dwuznakowe oznaczające koniec linii). Wyjątkiem jest tryb kropkowy (który omówimy dalej), w którym kropki dopasowują się również do separatorów linii. Klasa Pattern rozróżnia następujące separatory linii:
- znak powrotu karetki (
\r );
- znak nowej linii (symbol przesunięcia papieru o jedną linię) (
\n );
- znak powrotu karetki, po którym następuje znak nowej linii (
\r\n );
- znak następnego wiersza (
\u0085 );
- znak separatora linii (
\u2028 );
- symbol separatora akapitu (
\u2029 )
|
Przechwycone grupy
Grupa przechwytująca służy do zapisywania znalezionego zestawu znaków do dalszego wykorzystania podczas wyszukiwania według wzorca. Konstrukcja ta jest ciągiem znaków ujętych w metaznaki w nawiasach (
( )
). Podczas wyszukiwania według wzorca wszystkie znaki w przechwyconej grupie są traktowane jako jedna całość. Na przykład grupa przechwytywania (
Java
) łączy litery
J
,
a
i w jedną jednostkę. Ta grupa przechwytywania wyszukuje wszystkie wystąpienia wzorca w tekście wejściowym. Przy każdym dopasowaniu poprzednie zapisane znaki są zastępowane przez kolejne. Przechwycone grupy można zagnieżdżać w innych przechwyconych grupach. Na przykład w wyrażeniu regularnym grupa jest zagnieżdżona w grupie . Każdej zagnieżdżonej lub niezagnieżdżonej grupie przechwytywania przypisany jest numer, zaczynając od 1, a numeracja przebiega od lewej do prawej. W poprzednim przykładzie dopasowuje grupę przechwytywania numer 1 i grupę przechwytywania numer 2. W wyrażeniu regularnym dopasowuje grupę przechwytywania numer 1 i grupę przechwytywania numer 2. Dostęp do dopasowań przechowywanych przez grupy przechwytywania można później uzyskać za pomocą odwołań wstecznych. Określone jako ukośnik odwrotny, po którym następuje znak numeryczny odpowiadający numerowi przechwytywanej grupy, odwołanie wsteczne umożliwia odwoływanie się do znaków w tekście przechwyconym przez grupę. Posiadanie linku zwrotnego powoduje, że moduł dopasowujący odwołuje się do zapisanego wyniku wyszukiwania przechwyconej grupy w oparciu o znajdujący się w nim numer, a następnie używa znaków z tego wyniku do podjęcia próby dalszego wyszukiwania. Poniższy przykład ilustruje użycie odwołania wstecznego do znalezienia błędów gramatycznych w tekście: W tym przykładzie użyto wyrażenia regularnego do znalezienia błędu gramatycznego ze zduplikowanym słowem bezpośrednio po tekście wejściowym . To wyrażenie regularne określa dwie grupy do przechwycenia: numer 1 – , odpowiadający i numer 2 – , odpowiadający znakowi spacji, po którym następuje . Odwołanie wsteczne umożliwia ponowne przejrzenie zapisanego wyniku grupy nr 2, dzięki czemu funkcja dopasowująca może wyszukać drugie wystąpienie spacji, po której następuje , natychmiast po pierwszym wystąpieniu spacji i . Wyniki pojedynku przedstawiają się następująco:
v
a
Java
Java
(Java( language))
(language)
(Java)
(Java( language))
(language)
(a)(b)
(a)
(b)
java RegexDemo "(Java( language)\2)" "The Java language language"
(Java( language)\2)
language
Java
"The Java language language"
(Java( language)\2)
Java language language
(language)
language
\2
language
language
RegexDemo
regex = (Java( language)\2)
input = The Java language language
Found [Java language language] starting at 4 and ending at 25
Dopasowujące granice
Czasami trzeba wykonać dopasowanie wzorca na początku linii, na granicach wyrazów, na końcu tekstu itp. Można to zrobić, korzystając z jednego z elementów dopasowujących krawędzi klasy
Pattern
, które są konstrukcjami wyrażeń regularnych wyszukujących dopasowania w następujących lokalizacjach:
^
: Początek linii;
$
: Koniec linii;
\b
: Granica słowa;
\B
: Granica pseudosłowa;
\A
: Początek tekstu;
\G
: Koniec poprzedniego meczu;
\Z
: Koniec tekstu, z wyłączeniem separatora linii końcowej (jeśli występuje);
\z
: Koniec tekstu
W poniższym przykładzie zastosowano
^
metaznak dopasowujący granicę do wyszukiwania linii rozpoczynających się od
The
, po których następuje zero lub więcej znaków słowa:
java RegexDemo "^The\w*" Therefore
Znak
^
określa, że pierwsze trzy znaki tekstu wejściowego muszą odpowiadać kolejnym znakom wzorca
T
,
h
i
e
, po których może następować dowolna liczba symboli słowotwórczych. Oto wynik wykonania:
regex = ^The\w*
input = Therefore
Found [Therefore] starting at 0 and ending at 8
Co się stanie, jeśli zmienisz wiersz poleceń na
java RegexDemo "^The\w*" " Therefore"
? Nie zostanie znalezione żadne dopasowanie, ponieważ
Therefore
tekst wejściowy jest poprzedzony znakiem spacji.
Dopasowania o zerowej długości
Czasami podczas pracy z urządzeniami dopasowującymi krawędzie można spotkać dopasowania o zerowej długości.
Совпадение нулевой длины
to dopasowanie, które nie zawiera żadnych znaków. Mogą one występować w pustym tekście wejściowym, na początku tekstu wejściowego, po ostatnim znaku tekstu wejściowego oraz pomiędzy dowolnymi dwoma znakami tekstu wejściowego. Dopasowania o zerowej długości są łatwe do rozpoznania, ponieważ zawsze zaczynają się i kończą w tej samej pozycji. Rozważmy następujący przykład:
java RegExDemo \b\b "Java is"
Ten przykład wyszukuje dwie kolejne granice słów, a wyniki wyglądają następująco:
regex = \b\b
input = Java is
Found [] starting at 0 and ending at -1
Found [] starting at 4 and ending at 3
Found [] starting at 5 and ending at 4
Found [] starting at 7 and ending at 6
W wynikach widzimy kilka dopasowań o zerowej długości. Pozycje końcowe są tutaj o jeden mniejsze niż pozycje początkowe, ponieważ
RegexDemo
określiłem je w kodzie źródłowym na Listingu 1
end() – 1
.
Kwantyfikatory
Kwantyfikator to konstrukcja wyrażenia regularnego, która jawnie lub pośrednio wiąże wzorzec z wartością liczbową. Ta wartość liczbowa określa, ile razy należy szukać wzorca. Kwantyfikatory dzielą się na zachłanne, leniwe i superchciwe:
- Kwantyfikator zachłanny (
?
, *
lub +
) ma na celu znalezienie najdłuższego dopasowania. Czy mogę zapytać X
? znaleźć jedno lub mniej wystąpień X
, X*
znaleźć zero lub więcej wystąpień X
, X+
znaleźć jedno lub więcej wystąpień X
, X{n}
znaleźć n
wystąpienia X
, X{n,}
znaleźć co najmniej (i prawdopodobnie więcej) n
wystąpień X
oraz X{n,m}
znaleźć co najmniej , n
ale nie więcej m
wystąpień X
.
- Leniwy kwantyfikator (
??
, *?
lub +?
) ma na celu znalezienie najkrótszego dopasowania. Można określić X??
wyszukiwanie jednego lub mniejszej liczby wystąpień X
, X*
? znaleźć zero lub więcej wystąpień X
, X+?
znaleźć jedno lub więcej wystąpień X
, X{n}?
znaleźć n
wystąpienia X
, X{n,}?
znaleźć co najmniej (i prawdopodobnie więcej) n
wystąpień X
oraz X{n,m}?
znaleźć co najmniej , n
ale nie więcej niż m
wystąpienia X
.
- Kwantyfikator super zachłanny (
?+
, *+
lub ++
) jest podobny do kwantyfikatora zachłannego, z tą różnicą, że kwantyfikator super zachłanny podejmuje tylko jedną próbę znalezienia najdłuższego dopasowania, podczas gdy kwantyfikator zachłanny może podejmować wiele prób. Można ustawić X?+
, aby znaleźć jedno lub mniej wystąpień X
, X*+
znaleźć zero lub więcej wystąpień X
, X++
znaleźć jedno lub więcej wystąpień X
, X{n}+
znaleźć n
wystąpienia X
, X{n,}+
znaleźć co najmniej (i prawdopodobnie więcej) n
wystąpień X
oraz X{n,m}+
znaleźć co najmniej , n
ale nie więcej niż m
wystąpienia X
.
Poniższy przykład ilustruje użycie zachłannego kwantyfikatora:
java RegexDemo .*ox "fox box pox"
Oto wyniki:
regex = .*ox
input = fox box pox
Found [fox box pox] starting at 0 and ending at 10
Kwantyfikator zachłanny (
.*
) znajduje najdłuższą sekwencję znaków kończącą się na
ox
. Zużywa cały tekst wejściowy, a następnie wycofuje się do momentu wykrycia, że tekst wejściowy kończy się tymi znakami. Rozważmy teraz leniwy kwantyfikator:
java RegexDemo .*?ox "fox box pox"
jego wyniki:
regex = .*?ox
input = fox box pox
Found [fox] starting at 0 and ending at 2
Found [ box] starting at 3 and ending at 6
Found [ pox] starting at 7 and ending at 10
Leniwy kwantyfikator (
.*?
) znajduje najkrótszą sekwencję znaków kończącą się na
ox
. Zaczyna się od pustego ciągu i stopniowo zużywa znaki, aż znajdzie dopasowanie. A następnie kontynuuje pracę, aż do wyczerpania tekstu wejściowego. Na koniec spójrzmy na super-chciwy kwantyfikator:
java RegexDemo .*+ox "fox box pox"
A oto jego wyniki:
regex = .*+ox
input = fox box pox
Bardzo zachłanny kwantyfikator (
.*+
) nie znajduje dopasowań, ponieważ zużywa cały tekst wejściowy i
ox
na końcu wyrażenia regularnego nie ma już nic do dopasowania. W przeciwieństwie do kwantyfikatora zachłannego, kwantyfikator superchciwy nie cofa się.
Dopasowania o zerowej długości
Czasami podczas pracy z kwantyfikatorami napotkasz dopasowania o zerowej długości. Na przykład użycie następującego kwantyfikatora zachłannego skutkuje wieloma dopasowaniami o zerowej długości:
java RegexDemo a? abaa
Wyniki uruchomienia tego przykładu:
regex = a?
input = abaa
Found [a] starting at 0 and ending at 0
Found [] starting at 1 and ending at 0
Found [a] starting at 2 and ending at 2
Found [a] starting at 3 and ending at 3
Found [] starting at 4 and ending at 3
W wynikach wykonania znajduje się pięć dopasowań. Choć pierwsza, trzecia i czwarta są dość oczekiwane (odpowiadają pozycjom trzech liter
a
w
abaa
), druga i piąta mogą Cię zaskoczyć. Wydaje się, że wskazują, co
a
odpowiada
b
końcowi tekstu, ale w rzeczywistości tak nie jest. Wyrażenie regularne
a?
nie wyszukuje
b
na końcu tekstu. Szuka obecności lub nieobecności
a
. Jeśli
a?
nie znajdzie
a
, zgłasza to jako dopasowanie o zerowej długości.
Zagnieżdżone wyrażenia flagowe
Dopasowujące przyjmują pewne domyślne założenia, które można zastąpić podczas kompilowania wyrażenia regularnego we wzorzec. Omówimy tę kwestię później. Wyrażenie regularne pozwala zastąpić dowolne wartości domyślne za pomocą zagnieżdżonego wyrażenia flagowego. Ta konstrukcja wyrażenia regularnego jest określona jako metaznak złożony z nawiasów wokół metaznaku znaku zapytania (
?
), po którym następuje mała litera łacińska. Klasa
Pattern
rozumie następujące zagnieżdżone wyrażenia flagowe:
(?i)
: Włącza dopasowywanie wzorców bez uwzględniania wielkości liter. Na przykład podczas korzystania z polecenia java RegexDemo (?i)tree Treehouse
sekwencja znaków Tree
odpowiada wzorowi tree
. Wartość domyślna to wyszukiwanie wzorców z uwzględnieniem wielkości liter.
(?x)
: Zezwala na użycie białych znaków i komentarzy zaczynających się od metaznaku we wzorcu #
. Dopasowujący zignoruje oba. Przykładowo dla java RegexDemo ".at(?x)#match hat, cat, and so on" matter
ciągu znaków mat
odpowiada wzorowi .at
. Domyślnie białe znaki i komentarze są niedozwolone, a moduł dopasowujący traktuje je jako znaki biorące udział w wyszukiwaniu.
(?s)
: Włącza tryb kropkowy, w którym metaznak kropki dopasowuje separatory linii oprócz wszelkich innych znaków. Na przykład polecenie java RegexDemo (?s). \n
znajdzie znak nowej linii. Wartość domyślna jest przeciwieństwem dotall: nie zostaną znalezione żadne separatory linii. Na przykład polecenie Java RegexDemo . \n
nie znajdzie znaku nowej linii.
(?m)
: Włącza tryb wielowierszowy, w którym ^
dopasowuje początek i $
koniec każdej linii. Na przykład java RegexDemo "(?m)^abc$" abc\nabc
znajduje obie sekwencje w tekście wejściowym abc
. Domyślnie używany jest tryb jednowierszowy: ^
dopasowuje początek całego wprowadzonego tekstu i $
dopasowuje jego koniec. Na przykład java RegexDemo "^abc$" abc\nabc
zwraca odpowiedź, że nie ma żadnych dopasowań.
(?u)
: Włącza wyrównanie wielkości liter z uwzględnieniem Unicode. Ta flaga, używana w połączeniu z (?i)
, umożliwia dopasowywanie wzorców bez uwzględniania wielkości liter, zgodnie ze standardem Unicode. Domyślnym ustawieniem jest wyszukiwanie wyłącznie znaków z uwzględnieniem wielkości liter i znaków US-ASCII.
(?d)
: Włącza tryb ciągów w stylu uniksowym, w którym funkcja dopasowująca rozpoznaje metaznaki w kontekście .
i tylko separator linii . Domyślnym trybem jest tryb ciągów w stylu innym niż Unix: moduł dopasowujący rozpoznaje, w kontekście powyższych metaznaków, wszystkie ograniczniki linii.^
$
\n
Zagnieżdżone wyrażenia flagowe przypominają przechwycone grupy, ponieważ ich znaki są otoczone metaznakami w nawiasach. W przeciwieństwie do grup przechwyconych, wyrażenia flag zagnieżdżonych są przykładem grup nieprzechwyconych, które są konstrukcją wyrażenia regularnego, która nie przechwytuje znaków tekstowych. Definiuje się je jako ciągi znaków otoczone metaznakami w nawiasach.
Określanie wielu zagnieżdżonych wyrażeń flag |
Możliwe jest określenie wielu zagnieżdżonych wyrażeń flag w wyrażeniu regularnym, umieszczając je obok siebie ( (?m)(?i)) ) lub umieszczając litery je definiujące sekwencyjnie ( (?mi) ). |
Wniosek
Jak już zapewne zauważyłeś, wyrażenia regularne są niezwykle przydatne, a stają się jeszcze bardziej przydatne, gdy opanujesz niuanse ich składni. Jak dotąd zapoznałem Cię z podstawami wyrażeń regularnych i
Pattern
. W części 2 przyjrzymy się bliżej API Regex i poznamy metody metod
Pattern
,
Matcher
i
PatternSyntaxException
. Pokażę Ci także dwa praktyczne zastosowania Regex API, które możesz od razu zastosować w swoich programach.
Wyrażenia regularne w Javie, część 3 Wyrażenia regularne w Javie, część 4 Wyrażenia regularne w Javie, część 5
Co jeszcze warto przeczytać: |
|
GO TO FULL VERSION