Wyrażenia regularne w Perlu Narzędzia informatyczne w językoznawstwie Perl - Wyrażenia regularne Marcin Junczys-Dowmunt junczys@amu.edu.pl Zakład Logiki Stosowanej http://www.logic.amu.edu.pl 16. styczeń 2008 Na ostatnich zajęciach zapoznaliśmy się z Wyrażeniami Regularnymi na sucho, dziś omówimy ich integrację w Perlu. Na wykładzie dotyczącym haszów poznaliśmy jedno z najpotężniejszych narzędzi Perla, drugim (lub pierwszym?) najważniejszym mechanizmem tego języka są właśnie RE (Regular Expressions, będziemy stosowali taki skrót) RE same w sobie nie są może szczególnie potężne, ale w połączeniu z językiem programowania, można wykorzystać je do wielu różnych zadań Wyrażenia regularne w Perlu ustanowiły pewien standard: PCRE (Perl Compatible Regular Expressions) Większość szanujących się programów korzysta właśnie z tego standardu, inne standardy RE są raczej egzotyczne Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 1/20 Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 2/20 Pierwsze przykłady Przykład bardziej sensowny perlowy minigrep.pl 1 print " Dopasowano \n" if(" Hello World " =~ / World /); print " Nie dopasowano \n" if(" Hello World "!~ / World /); 5 $reg = World ; print " Dopasowano \n" if(" Hello World " =~ / $reg /); RE otaczamy w Perlu operatorami /.../ Porównując zmienną lub łańcuch z RE korzystamy z operatorów =~ (dopasowanie pozytywne) lub!~ (dopasowanie negatywne) Wewnątrz operatorów /.../ działa interpolacja zmiennych, trzeba pamiętać o związku cudzysłowu podwójnego ze znakiem \ 1 my $regexp = shift @ ARGV ; while (<>) { print if (/ $regexp /); Unixowy Grep to program, który wyświetla wiersze (i inne informacje) z pliku lub z plików tekstowych, które pasują do podanego wzorca, np. grep "\b[aa]dam\b" plik1.txt Nasz prosty program perlowy minigrep.pl działa podobnie, np. perl minigrep.pl "\b[aa]dam\b" plik1.txt Z tym, że nie informuje o nazwie pliku lub numerze wiersza (patrz zadanie domowe) Jak widać użycie samego operatora /.../ powoduje porównywanie ze zmienną standardową Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 3/20 Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 4/20
Sortowanie mieszane raz jeszcze Grupowania i odwołania do grup z poza RE 1 my @mieszane = (4," Antek ",13,9," Zenon ",2,10," Mirek "); print join (",", sort { is_num ( $a) && is_num ( $b)? $a <=> $b : $a cmp $b; @mieszane )."\n"; 5 sub is_num { my $test = shift ; return $test =~ /^[+ -]?\ d +(\.\ d +)?([ ee ][+ -]?\ d +)? $/; Nie potrafiliśmy sortować alfabetycznie z liczbami poprzedzającymi łańcuchy znakowe w porządku rosnącym Teraz potrafimy Funkcja is num sprawdza, czy dany łańcuch znakowy ma postać liczby 1 my $record = " Jan Kowalski, 20. 12. 1982 "; $record =~ /^(\ w +)\ s(\w+),\s([\ w.]+) $/; print "$2, $1 urodzil (a) sie dnia $3\n"; Zmienne specjalne $1 do $9 odwołują się odpowiednio do grup \1 do \9, jeśli takie grupy nie zostały dopasowane to zmienne zawierają undef Wewnątrz RE korzystamy z odwołań typu \1, w programach ze zmiennych typu $1 Dopasowanie w użyte w kontekście listowym powoduje przypisanie kolejnych grup do kolejnych elementów listy. 1 my $record = " Jan Kowalski, 20. 12. 1982 "; my @gr = $record =~ /^(\ w +)\ s(\w+),\s([\ w.]+) $/; print " $gr [1], $gr [0] urodzil (a) sie dnia $gr [2]\ n"; Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 5/20 Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 6/20 Nawiasy niegrupujące Grupy nazwane 1 my $number = " nasza liczba : -3.1105 e -010 "; $number =~ /([+ -]?\ d +(?:\.\ d +)?(?:[ ee ]([+ -]?\ d +))?)/; print " Liczba $1 ma wykładnik $2\ n"; Standardowo każda para nawiasów generuje nową grupę (grupy numerujemy według nawiasów otwierających, niezależnie od głębokości zagnieżdżeń) Gdy korzystamy z kwantyfikatorów może to być działaniem niepożądanym Wtedy korzystamy z nawiasów niegrupujących (?:...) 1 my $record = " Jan Kowalski, Joanna Nowak "; $record =~ /^((? < imie >\w+) (? < nazwisko >\w+)(, )?)+ $/; print "$+{ nazwisko, $+{ imie \n"; 18. grudnia 2007 pojawił się nowy Perl o numerze 5.10.0 Od tej wersji działają tzw. grupy nazwane Grupę nazwaną tworzymy za pomocą konstrukcji (?<nazwa>...) lub (? nazwa...) Wewnątrz RE odwołujemy się do nich przez \gnazwa W programie odwołujemy się do nich przez specjalny hasz %+, np. $+{nazwa; Nazwy mogą się powtarzać, ale możemy się odwołać tylko do ostatniego dopasowania Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 7/20 Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 8/20
Funkcja split - dzielenie rekordów i nie tylko 1 my $rec = " Kowalski Jan 20-12 -1984\ n"; my ( $nazw, $imie, @data ) = split (/\ s+ \ -/, $rec ); print " $imie $nazw urodzil sie w roku $data [2]\ n"; Funkcja split jest jedną z bardziej przydatnych funkcji w Perlu, łącząca w sobie funkcjonalność RE i list (przeciwieństwo funkcji join) Dzieli dany łańcuch znakowy na pola według podanego RE Jeśli podane RE nie zawiera nawisów, to części dopasowane nie znajdą się w liście wynikowej Jeśli RE zawiera nawiasy grupujące, to dopasowane grupy znajdują się w listach pomiędzy polami 1 my $rec = " Kowalski Jan 20-12 -1984\ n"; my ( $nazw, $imie, @data ) = split (/\ s + (\ -)/, $rec ); print join (":", @data )."\n"; Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 9/20 Program tworzący listę frekwencynją raz jeszcze 1 my % freq ; while (<>) { my @tokens = split (/[\ s.,;?!:\ -()]+/, $_ ); foreach my $token ( @ tokens ) { 5 $freq { $token ++; foreach my $token ( sort keys % freq ) { print " $token $freq { $token \n"; 10 W końcu znamy wszystkie narzędzia potrzebne do napisania programu zliczającego liczbę wystąpień wyrazów w tekście Korzystamy tutaj prawie ze wszystkich mechanizmów poznanych na wcześniejszych wykładach W jakim porządku jest wyświetlana lista frekwencyjna? Jaki porządek byłby bardziej przydatny? Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 10/20 Flagi Flaga i (case) insensitive 1 my $tekst = " dziwna pisownia " 1 $zmienne =~ / wzorzec / msigx Zachowywanie domyślne RE możemy modyfikować za pomocą tzw. flag Flagi mają postać pojedynczych liter umieszczanych bezpośrednio za operatorami RE /.../ Flagi można ze sobą łączyć, przy czym niektóre kombinacje są szczególnie pożyteczne if( $tekst =~ / pisownia /i) { print " Dopasowanie sie powiodlo \ n"; 5 Flaga i włącza tryb dopasowywania bez rozróżniania wielkości liter Flagę i należy stosować oszczędnie, ponieważ spowalnia proces dopasowywania RE Jeśli istnieje taka możliwość, to lepiej korzystać np. z odpowiednich klas znaków (np. gdy dopuszczalne są duże i małe litery na początku wyrazu) Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 11/20 Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 12/20
Flaga g global Zastępowanie 1 my $tekst = " The Messenger spacecraft dashed past Mercury today "; my @tokens = $tekst =~ /([\ w \ -]+)/ g; 5 print "$_\n" foreach ( @tokens ); Domyślnie RE dopasują pierwsze możliwe wystąpienie szukanego wzorca i tylko pierwsze wystąpienie Flaga g powoduje, że dopasowane zostaną wszystkie pasujące wystąpienia wzorca Gdy jedno wystąpienie zostanie dopasowane, następne wystąpienie zostanie dopasowane dopiero za końcem poprzedniego dopasowania Jeśli w takim RE wykorzystamy nawiasy grupujące, najłatwiej odwołać się do grup poprzez tablicę 1 $_ = " green scaly dinosaur "; s /(\ w+) (\w+)/ $2, $1 /; # scaly, green dinosaur s /^/ huge, /; # huge, scaly, green dinosaur s/,.* een //; # huge dinosaur 5 s/ green / red /; # huge dinosaur s/\w+$/($!) $ &/; # huge ( huge!) dinosaur s/\s +(!\ W +)/ $1 /; # huge ( huge!) dinosaur s/ huge / gigantic /; # gigantic ( huge!) dinosaur Operator zastępowania s/.../.../ składa się dwóch części: W pierwszym polu znajduje się dowolne wyrażenie regularne W drugim polu znajduje się dowolny łańcuch znakowy (działający jak podwójnym cudzysłowu), a nie wyrażenie regularne Korzystamy ze zmiennych typu $1 a nie z odwołań typu \1 Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 13/20 Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 14/20 Zastępowanie globalne oraz ewaluacja Transliteracja 1 $tekst =~ s /([+ -]?\ d +(?:\.\ d +)?)/ log ($1 )/ eg; Większość flag działa również w połączeniu z operatorem zastępowania Flaga g spowoduje zastąpienie wszystkich dopasowań w łańcuchu, nie tylko pierwszego Flaga e pozwala na wpisywanie kodu Perla do drugiego pola zastępowania. Tak naprawdę flaga e otacza drugie pole funkcją eval(), która wykonuje kod Perla zapisany w łańcuchu znakowym 1 my $normalizacja = " miłośnik żab "; $normalizacja =~ tr/ ąćęłńóśżź / acelnoszz /; print $normalizacja."\n"; Transliteracja z operatorem tr/.../.../ często pojawia się w rozdziałach dotyczących RE, jednak tak naprawdę nie działa jak RE Pola operatora zawierają klasy znaków Każdy znak z pierwszego pola jest zastępowany znakiem na tej samej pozycji w klasie znaków z drugiego pola Gdy drugie pole zawiera mniej znaków niż pierwsze, to ostatni znak z drugiego pola dopełnia różnicę Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 15/20 Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 16/20
Zachowanie kropki a flagi s i m Flaga o Kompilacja wzorców Zachowanie standardowe.. dopasuje wszystkie znaki oprócz \n. ^ dopasuje tylko początek łańcucha, a $ tylko koniec łańcuchy lub przed \n na końcu łańcucha Flaga s: Traktuje łańcuch znakowy jak jedną długą linię.. dopasuje wszystkie znaki, łącznie z \n. ^ dopasuje tylko początek łańcucha, a $ tylko koniec łańcuchy lub przed \n na końcu łańcucha Flaga m: Traktuje łańcuch znakowy jak zbiór wierszy.. dopasuje wszystkie znaki oprócz \n. ^ i $ mogą dopasować początek i koniec każdego z wierszy. Flagi s i m: Traktuje łańcuch znakowy jak jedną długą linię, ale wykrywa obecność wielu wierszy.. dopasuje wszystkie znaki, łącznie z \n. ^ i $ mogą dopasować początek i koniec każdego z wierszy. 1 my @ miesiace = qw( stycznia lutego marca...); my $pat = join (" ", @miesiace ); if(" 16. stycznia 2008 " =~ /\d\d\. $pat \d {4/ o) { 5 print " Dopasowano \n"; Flaga o kompiluje nasze RE, tzn. że RE jest optymalizowane i staje się niezmienne (!) Optymalizację warto stosować, gdy wewnątrz RE odbywa się interpolacja zmiennych Proces dopasowywania przyspieszy wtedy znacznie Uwaga: Międzyczasowe zmiany zmiennej interpolowanej nie zmienią wzorca, będzie on miał postać, którą miał przy pierwszej kompilacji Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 17/20 Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 18/20 Flaga x extended RE 1 my $number = " nasza liczba : -3.1105 e -010 "; $number =~ / ( [+ -]? # opcjonalny znak 5 \ d + # część całkowita mantysy (?:\.\ d +)? # opjconalna część ułamkowa mantysy (?: [ee] ([+ -]?\ d +) # wykładnik z opcjonalnym znakiem 10 )? # wykładnik też jest opcjonalny ) /x; print " Liczba $1 ma wykładnik $2\ n"; Podsumowanie Poznaliśmy kilka podstawowych własności Wyrażeń Regularnych używanych w Perlu Jest jeszcze wiele własności, flag, znaków specjalnych, których jeszcze nie umówiliśmy: np. tzw. Look-Ahead Zachęcam bardzo do przeczytania innych źródeł, np. oficjalnej dokumentacji do Wyrażeń Regularnych w Perlu. Linki znajdują się na stronie przedmiotu Flaga x pozwala na bardziej czytelny zapis RE Białe znaki oraz komentarze są ignorowane Jeśli chcemy dopasować spacje lub # korzystamy z \ oraz \# Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 19/20 Marcin Junczys-Dowmunt Narzędzia informatyczne w językoznawstwie 20/20