Przeanalizujmy kilka wyrażeń regularnych aby lepiej zrozumieć ich działanie.
Kropka jest znakiem specjalnym, który w wyrażeniu regularnym oznacza dowolny pojedynczy znak. Korzysta się z kropki zawsze wtedy, kiedy się chce dopasować pojedynczy znak, ale nie jest to istotne jaki to będzie znak. Na przykład wzorzec, który można dopasować do wszystkich trzyliterowych napisów rozpoczynających się od litery t i kończących się literą k, będzie miał następującą postać
t.k
Do tego wzorca będą pasować nie tylko napisy: tak, tik, tor, które tworzą słowa mające jakiś sens, ale także słowa takie jak tzk, tyk, twk, które znaczenia nie mają oraz dłuższe napisy które zawierają wzorzec: t.k
Ratakomb
Tortaktor
Należy podkreślić, że kropka odpowiada dowolnemu znakowi ASCII nie tylko literom lub cyfrom.
Powiedzmy, że chcemy przeszukać plik, który zawiera pięcioznakowe kody części jakiegoś urządzenia. Chcemy wyszukać pięcioznakowe napisy w których na trzecim miejscu znajduje się litera z. Odpowiednie wyrażenie regularne będzie miało postać:
..z..
Wyrażenie, które pozwala w tekście wyszukać linie białe (niezapisane) ,ma postać następującą:
^$
Dopasowywanie zbioru znaków za pomocą symbolu [ ]
W tym wypadku wyrażenie regularne jest dopasowane, jeżeli dowolny ze znaków zbioru wystąpi w sprawdzonym zapisie umieszczonym między [ ]. Wróćmy do przykładu napisu trzyznakowego, który rozpoczyna się od litery t i kończy literą k. Widzieliśmy uprzednio, że spowodowała, ze ten wzorzec odpowiadał wielu napisom. Jeżeli naszym zamiarem jest dopasowanie tylko określonych trzyznakowych słów, które rozpoczynają się od t i kończą k, moglibyśmy użyć [ ] aby zawęzić poszukiwania. Załóżmy, że chcielibyśmy zawęzić poszukiwania do małych liter. Moglibyśmy zamieścić w nawiasach [ ] alfabet małych liter, ale na szczęście możemy tez użyć skrótu. Znak - reprezentuje zakres. A więc zakres [a-z] reprezentuje [abcdefghijk…z]. stąd wynika, że nasze wyrażenie regularne będzie miało postać:
t[a-z]k
W ten sposób wyeliminujemy wszystkie napisy , które nie mają małej litery na drugiej pozycji. Możemy przyjąć założenie, że na drugiej pozycji znajdować się muszą tylko samogłoski:
t[aeiou]k
Konstrukcja [ ] jest bardzo przydatna w wyszukiwaniu odpowiednich ciągów znakowych w plikach. Za pomocą operatora zakresu, można określić dowolna sekwencje znaków, o ile są to znaki umieszczone kolejno po sobie w tabeli kodów ASCII. Na przykład [B-D] reprezentuje wielkie litery BCD. Poniżej przedstawiamy kilka częściej używanych zakresów
[A-Z]
[a-z]
[0-9]
[A-Za-z]
Szczególnie ważnym operatorem specjalnym (oprócz już omówionego operatora zakresu - ) jest tzw. operator dopełnienia zbioru ^ . Jeżeli pierwszym znakiem po lewym nawiasie jest operator ^ wtedy dopasowywane jest dopełnienie zbioru znaków zawartych między nawiasami [ ]. Oznacza to, ze wzorcowi będzie odpowiadać każdy znak, który nie znajduje się w tym zbiorze. Na przykład wyrażenie regularne:
t[^A-Z]a
będzie odpowiadało każdemu trzyznakowemu napisowi rozpoczynającemu się od t kończącemu na a i niezawierającemu na drugiej pozycji wielkiej litery. Operator ^ można zastosować do wyszukiwania najróżnorodniejszych ciągów znaków np. wyrażenie:
[^A-Za-z]
będzie odpowiadało wszystkim znakom nie będącymi literami.
I ostatnia uwaga związana z konstrukcją [ ] : wszystkie znaki występujące w nawiasach maja zwykłe znaczenie. Jedynymi znakami, które maja znaczenie specjalne w konstrukcji [ ] są : operator zakresu - i operator dopełnienia zbioru ^ . Oczywiście aby użyć ich jako zwykłych znaków należy je poprzedzić znakiem neutralizującym znaki specjalne: \ . Na przykład, jeżeli chcemy wyselekcjonować w pliku wszystkie trzyznakowe ciągi zaczynające się od litery A a kończące się literą B gdzie na drugiej pozycji znajduje się znak ^ , to musimy to zapisać:
[A\^B]
Dopasowywanie początku wiersza za pomocą symbolu ^
Dopasowywanie zera lub większej liczby znaków za pomocą symbolu *
Znak * jest używany do dopasowania zera lub większej liczby wystąpień poprzedzającego znaku (znaków) lub wyrażenia regularnego. Zauważmy, że znaczenie tego znaku jest inne niż w przypadku generowania nazw plików, gdzie znak * oznaczał zastąpienie dowolnym ciągiem znaków (taki wzorzec można uzyskać w wyrażeniach regularnych za pomocą zapisu .*, który oznacza powtórzenie dowolnego znaku dowolna liczbę razy). Znak * w wyrażeniach regularnych oznacza tylko powtarzanie. Poprzedni znak może być powtórzony dowolna liczbę razy.
Przejrzyjmy się następującemu wyrażeniu regularnemu:
OP*A
Wzorzec ten będzie odpowiadać zarówno zapisowi OA jak i zapisowi OPPPPPPPA. Jeżeli chcemy aby co najmniej jedno P wystąpiło w dopasowanym napisie wyrażenie regularne musi przyjąć postać :
OPP*A
W tym przypadku najkrótszy dopasowany zapis będzie miał postać: OPA. Znak * możemy umieścić za dowolnym wyrażeniem regularnym w celu wskazania że ma być ono powtórzone zero lub większą ilość razy. Oto kilka często używanych wyrażeń regularnych z symbolem * :
[A-Za-z] [A-Za-z]* → wzorzec dla dowolnego zapisu, odpowiada wszystkim możliwym słowom.
[+ -] [0-9] → wzorzec dla dowolnej liczby całkowitej poprzedzonej znakiem + lub –
.* → wzorzec dla dowolnego ciągu znaków.
^ *$ → wzorzec dla wierszy zawierających tylko spacje.
używamy w nim tylko znaku ^ i $ , które oznaczają początek i koniec linii (wiersza). Wyrażenie wskazuje, że pomiędzy początkiem i końcem linii nie ma nic, co odpowiada linii całkowicie niezapisanej. Porównajmy teraz dwa wyrażenia regularne i spróbujmy dokładnie określić różnice zachodzące pomiędzy nimi:
WR1 → ^.*$ : WR2 → ^.+$
W wyrażeniu WR1 na 3 pozycji znajduje się gwiazdka natomiast w wyrażeniu WR2 na tej samej pozycji znajduje się znak plus, co zmienia radykalnie zakres działania obu wyrażeń: WR1 wyszuka zarówno linie zapisane jak i niezapisane natomiast WR2 selekcjonuje tylko linie zapisane. Dlaczego?
(spróbuj odpowiedzieć na to sam)
Spróbujmy znaleźć znaczenie następującego wyrażenia regularnego:
^\* [0-9]*\. [0-9][0-9]\$.*$
Jak widzimy powyższe „wyrażenie regularne”, biorąc pod uwagę, że jest ono skomponowane z samych wieloznacznych symboli, może stać się całkowicie niezrozumiałe: jedyna metoda by odszyfrować (jak w podanym przykładzie) jego znaczenie polega na dekompozycji wyrażenia na mniejsze fragmenty poczynając od strony lewej i posuwając się w prawo. Postępując w ten sposób nasz przykład możemy przedstawić w następującej formie:
1 2 3 4 5 6 7 8 9
^..... \*..... __..... [0-9]* .....\. ..... [0-9][0-9] .....\$ ......* ..... $
1. Na pierwszym miejscu ^, który oznacza, że poszukiwany ciąg znaków znajduje się na początku wiersza (linii)
2. Na drugim miejscu znajdujemy wyrażenie \*, które znaczy, że pierwszy znak z tego ciągu jest „* ” (czy wiesz dlaczego ?)
3. Na trzecim miejscu w szukanym ciągu znaków znajduje się spacja
4. Na czwartym miejscu wyrażenie [0-9]* oznacza obojętnie jaką liczbe lub jej brak
5. Na piątym miejscu znajdujemy wyrażenie \., które oznacza że piątym znakiem ciągu jest „.”
6. Na szóstym miejscu powinniśmy znaleźć liczbe dwucyfrową
7. Na siódmym miejscu wyrażenie \$ oznacz, że w tym miejscu musi koniecznie znajdować się znak monetarny dolara : „$”
8. Na ósmym miejscu znajdujemy wyrażenie, które oznacza jakakolwiek liczba znaków (może być zero)
9. Na dziewiątym miejscu znajdujemy $, który oznacza koniec linii znak ten powinniśmy rozpatrywać wspólnie z poprzedzającym go wyrażeniem .*, które jak pamiętamy oznacza: jakąkolwiek liczbę znaków z tego wynika, że wyrażenie .*$ oznacza jakąkolwiek liczbę znaków do końca wiersza.
Z powyższej analizy wynika jeden bardzo ważny wniosek: wyrażenie .* można używać jako wyrażenie regularne, lecz trzeba mieć świadomość, że używane w formie wyizolowanej, jego użyteczność nie ma większego znaczenia, ponieważ odpowiada ono poszukiwaniu pustego ciągu znaków. Inaczej mówiąc poszukiwania ciągu znaków korespondującego wyrażeniu .* zanim się rozpoczną już się kończą znajdując pusty ciąg znaków
Rozszerzone wyrażenia regularne
+ → znak plus służy do wyszukania co najmniej jednego go poprzedzającego, tak więc a+ oznacza jedna lub wiele liter a, natomiast a* wyszykuje 0 lub więcej znaków a
( ) → nawiasy służą do grupowania wyrażeń regularnych np. (bla)* pozwala znaleźć w tekście bla i blabla blablabla etc.
? → pozwala wyszukać 0 lub jedną obecność zdefiniowanego ciągu znaków. Tak więc wyrażenie (\.com)? pozwala wyszukać nazwy plików kończące się na .com, przy czym nieobecność pliku o takim zakończeniu jest dopuszczalna (0 lub…)
| → pozwala wyszukiwać ciągi znaków alternatywne
Wyrażenia Regularne Rozdział 8.1 Darmowy kurs Linux