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 :-)

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

0 nowych
Jim1961   7 #1 03.03.2014 23:24

Najlepszy jest RegExp do sprawdzania poprawności adresu e-mail: http://ex-parrot.com/~pdw/Mail-RFC822-Address.html :D

  #2 04.03.2014 10:34
aPoCoMiLogin   7 #3 04.03.2014 10:45

IMO opisujesz wyrażenia regularne jakby były jakimś świętym gralem do wszystkiego i tak jakby dało się wyciągnąć wszystko ze wszystkiego, tyle że tak nie do końca to wygląda. Nie jestem jakimś super ultra dobry w wyrażeniach, ale swego czasu bardzo lubiłem używać wyrażeń i to czego się nauczyłem niewiele ma się do tego jak ty opisujesz wyrażenia.

Bo widzisz, "coś" z "czegoś" wyciągnąć możesz, pod warunkiem że ma to jakieś regularne wzorce, dlatego też wyrażenia nazywają się regularnymi. Ponieważ dostrzegasz wzorzec, coś w rodzaju kotwicy, i operujesz na tym. W zlepku znaków które nic nie znaczą, i są losowe takiego wzorca może nie być, więc wskazywanie sobie palcem i pytanie o radę doświadczonego programisty nie zawsze może dać wynik pozytywny.

Wyrażenia regularne to coś bardziej logiczna gra, w której się szuka kotwicy i używa odpowiednich narzędzi.

Jak to są twoje początki z regexp'em, to polecam poczytać o przewidywaniach w wyrażeniach, to zobaczysz jak bardzo wyrażenia staną się bardziej oczywiste i proste. A potem pisanie powiedzmy gdzieś na forum że użyłeś "przewidywania negatywnego wstecznego" w tym regexp'ie da ci z pewnością +10 pkt do zajebistości na forum ;)

LordRuthwen   5 #4 04.03.2014 11:41

Dobrze, że wspomniałeś o regexpach, ale to co wyciągasz z przykładu łatwiej i szybciej jest zrobić awkiem :)

wojtekadams   18 #5 04.03.2014 11:47

@GBM
@aPoCoMiLogin dobrze gada :)

zaś co do samych RegExpów to często z nich korzystam i warto nauczyć się nawet podstaw :)

GBM MODERATOR BLOGA  19 #6 04.03.2014 13:00

@aPoCoMiLogin: Masz sporo racji, "kotwice" i ich szukanie w treści do sparsowania - jest podstawą do stworzenia odpowiednich wzorców.

Zaś czy traktuję to jako święty graal? Możliwe, de facto wynika to z tego, że będąc na stażu w jednej takiej firmie, gdzie był bardzo rozbudowany działa administratorów systemów - regexpy były codziennością. I właśnie tam, zobaczyłem jak wzorce "powstawały z palca" :D
Nie jestem w stanie Ci przytoczyć, na czym dokładnie prezentowano mi te wzorce, niestety... Ale kotwice na pewno też, nie trudno było tam znaleźć ;p

@LordRuthwen: wiem, awk wymiata :-)
W sumie tak samo jak grep, którego również można by tutaj wykorzystywać pisząc jakiegoś jednolinijkowca ;p

@wojtekadams: no fakt, regexpy mogą być naprawdę przydatne :-)

GBM MODERATOR BLOGA  19 #7 04.03.2014 13:01

@aPoCoMiLogin: Masz sporo racji, "kotwice" i ich szukanie w treści do sparsowania - jest podstawą do stworzenia odpowiednich wzorców.

Zaś czy traktuję to jako święty graal? Możliwe, de facto wynika to z tego, że będąc na stażu w jednej takiej firmie, gdzie był bardzo rozbudowany działa administratorów systemów - regexpy były codziennością. I właśnie tam, zobaczyłem jak wzorce "powstawały z palca" :D
Nie jestem w stanie Ci przytoczyć, na czym dokładnie prezentowano mi te wzorce, niestety... Ale kotwice na pewno też, nie trudno było tam znaleźć ;p

@LordRuthwen: wiem, awk wymiata :-)
W sumie tak samo jak grep, którego również można by tutaj wykorzystywać pisząc jakiegoś jednolinijkowca ;p

@wojtekadams: no fakt, regexpy mogą być naprawdę przydatne :-)

FaUst   11 #8 04.03.2014 16:46

Przydatne i wkurzające kiedy są potrzebne a robi się to raz na pół roku ;)

  #9 04.03.2014 17:02

regexpy czasami są potrzebne,
ale wolałbym posługiwać się czymś innym.

Każda strona jest zbudowana ze strony html.
Dzięki odpowiednim narzędziom webowym w przeglądarce mogę
skopiować "ścieżkę"
(kod html lub inny, wskazując element prawie z palca, znaczy wizualnie bez kodu)
dowolnej rzeczy która jest na ekranie.
Dzięki odpowiedniej bibliotece programistycznej mógłbym
- pobrać dowolny fragment strony,
- a nawet wykorzystać jej treść np. interaktywne przyciski, linki.
Najczęstrzymi tego typu narzędziami wykorzystujące podobne rzeczy
chyba są boty, araz inne automaty, jeśli coś pokręciłem, proszę mnie poprawić.

Frankfurterium   9 #10 04.03.2014 18:39

Starożytna Chińska mądrość mówi:
"Pewnego dnia uczeń napotkał poważny problem. Po zastanowieniu postanowił rozwiązać go RegExem. Od tamtej chwili miał już dwa problemy."

W małych skrypcikach parsujących statyczny tekst są całkiem ok. W poważnych programach są strasznie mulaste i ekstremalnie zaciemniają kod. A za parsowanie nimi z łapy HTML-a idzie się do piekła...

soanvig   9 #11 04.03.2014 22:50

Nie podoba mi się to, że w regexpach nie mogę wstawić negacji występowania całego słowa :I

kostek135   8 #12 05.03.2014 02:48

Hmm napisz mi wyrażenie regularne, które powie czy dla pewnego alfabetu L = {a, b}, który generuje język taki, że należą do niego słowa postaci: a^nb^n i n > 0 (np. ab, aabb, aaabbb, etc.), dowolne słowo zbudowane przy użyciu alfabetu L jest poprawne w rozumieniu tego języka. Niby siła jest, ale jednak prosty przykład wykłada regex-a... To tak gwoli hura-optymizmu we wpisie.

GBM MODERATOR BLOGA  19 #13 05.03.2014 14:55

@tele1234567891 @Frankfurterium:

Dlatego ktoś stworzył Beautiful Soup dla Pythona - świetnie się z tego korzysta, jest banalne, a do tego - ma mnóstwo sposobów na HTML'a ;-)

http://www.crummy.com/software/BeautifulSoup/bs4/doc/

@kostek135: spoko, ale ja nadal zostanę przy moim "hura-optymizmie" ;-)