Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

Wyrażenia regularne tudzież RegExp'y – warta poznania przyjemność :-)

Wyrażenia regularne, są to specjalne funkcjonalności/wzorce, możliwe do zastosowania w wielu językach programowania. Ostatnimi czasy częstokroć miałem okazję do wykorzystywania ich przy tworzeniu różnych skryptów w Perlu bądź Pythonie. Miały być one pomocne najczęściej przy wyciąganiu odpowiednich danych z dostępnego źródła (np. logi systemowe). Ich znaczenie jest naprawdę bardzo istotne w takich rzeczach, ponieważ mają wielki potencjał w zakresie parsowania tekstu.

”Pokaż mi co i jak, a uwierz mi, że to wyciągnę!”

Czyli zacznijmy od praktyki...

Ale ważna sprawa na początek - dopiero od jakiegoś czasu przy różnych okolicznościach wykorzystuję wyrażenia regularne (zwane też „regexp'ami”), dlatego w temacie jestem dopiero raczkujący. Możliwe, że nie wniosę nic nowego, ale na pewno będę chciał zachęcić do przyjrzenia się z bliska omawianemu tematowi. A nuż kogoś zachęci, albo dowie się o czymś nowym :-)

r   e   k   l   a   m   a

Przykład, który chcę przedstawić i omówić w tym wpisie to parsowanie logów z serwera Apache. Nie jest to może bardzo oryginalny pomysł, a jednocześnie mało profesjonalny – ale myślę, że każdemu przypasuje – a ja skorzystam z czegoś, co działa dobrze i jest polecane. Dlatego uniknę wszelkich pomyłek, a tym samym nie wprowadzę nikogo w błąd ;-)

Na początku, zobaczmy co jest naszym źródłem i jakie mamy wobec niego zamiary.

89.200.145.199 - - [03/Mar/2014:20:12:14 +0100] "GET /wp-content/themes/e-Minimalist/images/bg-nav.png HTTP/1.1" 200 1056 "http://lochtcant.pl/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.59.10 (KHTML, like Gecko) Version/5.1.9 Safari/534.59.10"

Jak widzimy, jest to zbiór pewnych części składających się na wynik – czyli pojedynczy wiersz z informacjami. Jeden wiersz odpowiada jednemu "klientowi", którego zarejestrował Apache na danej stronie – podaje także najważniejsze informacje m.in. czas, źródło, referrer (źródło odwiedzin), User-Agent'a itd. Dodatkowo, oprócz tego podaje inne (generalnie mniej istotne z naszego punktu widzenia informacje – np. kody błędów) informacje, które akurat chcemy pominąć...

Naszym celem, jest rozbić każdy wiersz na jego części składowe i wyciągnąć tylko te, które nas interesują. Co prawda możemy zrobić to prościej i wyciągać od razu te informacje, które nas rzeczywiście interesują.
Ale po pierwsze, to faktycznie taki parser może się komuś przydać – będzie więc, miał od razu pakiet "wiedzy", a po drugie - przytoczone wyrażenia nie stworzyłem ja, tylko pewien admin na portalu StackOverflow więc... z poszanowaniem dla niego, nie chcę zmieniać kodu :-)


my ($ip, $date, $method, $url, $protocol, $alt_url, $code, $bytes,
        $referrer, $ua) = (m/
    ^(\S+)\s                    # IP
    \S+\s+                      # remote logname
    (?:\S+\s+)+                 # remote user
    \[([^]]+)\]\s               # date
    "(\S*)\s?                   # method
    (?:((?:[^"]*(?:\\")?)*)\s   # URL
    ([^"]*)"\s|                 # protocol
    ((?:[^"]*(?:\\")?)*)"\s)    # or, possibly URL with no protocol
    (\S+)\s                     # status code
    (\S+)\s                     # bytes
    "((?:[^"]*(?:\\")?)*)"\s    # referrer
    "(.*)"$                     # user agent
/x);

W podanym tutaj przykładzie, każdy wzorzec znajduje się w osobnej linii, a po nim w komentarzu zaznaczone jest, do jakiej części logu apache'a odnosi się.

Ok, mamy wyrażenia – ale jak je teraz wykorzystać, żeby uzyskać wynik np. w konsoli? Wystarczy kilka linijek poniżej :-)
Warto zauważyć, że istnieje bardzo fajne udogodnienie w Perlu - wzorce zamieszczone w nawiasach - można po kolei wypisać za pomocą zmiennych $1, $2, $3 itd.


  print "Adres IP: ".$1; # adres IP
  print "\n Data i godzina: ".$2; # data i godzina
  print "\n Zasób: ".$4; # URL (zasób)
  print "\n Źródło: ".$9; # referrer (źródło)
  print "\n User-Agent: ".$10; # User-Agent

A całość kodu wygląda następująco:


#!/usr/bin/perl  

open(logi, 'access_blog.log');

foreach $line (<logi>)
{
  if ($line =~ m/
		^(\S+)\s      # IP (IP)
		\S+\s+        #  nazwa użytkownika udostępniana przez system zdalny 
		(?:\S+\s+)+   # nazwa użytkownika podawana w procesie autoryzacji
		\[([^ ]+)[^"]+        # data i godzina (DATA)
		"(\S*)\s?             # metoda
		(?:((?:[^"]*(?:\\")?)*)\s     # URL
		([^"]*)"\s|((?:[^"]*(?:\\")?)*)"\s)   # żądany URL (ZASÓB) - (wzorzec podaje protokół albo możliwy URL bez pro$
		(\S+)\s       # kod odpowiedzi
		(\S+)\s       # liczba wysłanych bajtów przez serwer
		"((?:[^"]*(?:\\")?)*)"\s      # referrer (adres odsyłający) (ŹRÓDŁO)
		"(.*)"$                       #user-Agent (USER-AGENT)
		/x  )
	{
		print "\n \n";
		print " Adres IP: ".$1; # adres IP
		print "\n Data i godzina: ".$2; # data i godzina
		print "\n Zasób: ".$4; # URL (zasób)
		print "\n Źródło: ".$9; # referrer (źródło)
		print "\n User-Agent: ".$10; # User-Agent
	}
}

Efekt poniżej :-)

Gotowe, wystarczy kilka linijek i możemy już spokojnie wyciągać interesujące nas dane ;-)

Pokaz praktyki już był, więc czas na teorię..

Siła i potencjał wyrażeń regularnych

Tkwi w tym, że możemy wyciągać co nam się żywnie podoba. Doświadczeni programiści z jakimi miałem styczność, którzy do bólu zjedli już zęby na tworzeniu skryptów, które wykorzystywały do przeróżnych celów wzorce wyrażeń regularnych – potrafili mi w ciągu dosłownie kilku minut wyciągnąć to, co wskazałem sobie palcem. A jak sami widzicie, znaków i wzorców mogą być setki w przeróżnych kombinacjach. Dopiero wieloletnia praktyka pozwala biegle posługiwać się nimi.

Oto podstawowe znaki używane w wzorcach: (źródło )

Dodatkowo warto jeszcze rzucić okiem na:

Kombinacje możliwe do zastosowania przy wykorzystaniu powyższych przedstawionych składowych dla wzorców, naprawdę potrafią zdziałać cuda (a to jest dopiero kropla w morzu ;>). Regularna praktyka zdecydowanie pozwoli nam dostrzec potencjał i siłę wyrażeń regularnych, które sporo pomagają w codziennej pracy. Zwłaszcza tej administratorskiej :-)

Mam nadzieję, że choć trochę zachęciłem do rzucenia okiem, a może nawet i napisania jakiegoś prostego skryptu na spróbowanie tematu. Ręczę Wam, że jest to bardzo przydatna do przyswojenia wiedza.

Pozdrawiam,
GBM. 

linux programowanie serwery

Komentarze