PHP kurs: Uvod u regularne izraze

Started by Bred, 06-10-2006, 09:49:37

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Bred


Bez obzira na prirodu projekta na kome radite, pre ili kasnije doæi æete u situaciju da morate manipulisati nekim tekstualnim podacima. Bilo da je u pitanju jednostavna validacija forme ili pak parsovanje podataka sa nekog live sajta, regularni izrazi (eng. Regular Expressions) su oruðe kojim morate ovladati da bi ste bili uspe¹ni u tome.

Autor: Dragan Diniæ

Nivo teksta: Srednji

O va¾nosti regularnih izraza dovoljno govori èinjenica da je podr¹ka za njih prisutna u svim modernim programskim jezicima (u Perlu su èak deo samog jezika), a mo¾ete ih koristiti i u veæini naprednih tekst editora, alatima za pretragu itd.

Potpuno ovladavanje regularnim izrazima nije ne¹to ¹to mo¾ete "odraditi" preko noæi, ¹ta vi¹e, tema je toliko op¹irna da je o njoj napisano i nekoliko knjiga. Cilj ovog tutorijala je da vas upozna sa osnovnim moguænostima regularnih izraza kako biste bili u moguænosti da ih efikasno koristite.

©ta su to regularni izrazi

Regularni izraz praktièno predstavlja poseban skup znakova (string) u kome se odgovarajuæom sintaksom (eng. pattern) taj niz uporeðuje sa nekim drugim skupom znakova. Mo¾e se koristiti za pretragu unutar nekog teksta, izvlaèenje odreðenog podstringa, validaciju (e-maila na primer) i sl. Mo¾da sve ovo zvuèi komplikovano, ali æe stvar biti mnogo jasnija, èim krenemo sa nekoliko primera.

PHP podr¾ava takozvane POSIX kao i Perl Kompatibilne regularne izraze. Iako meðu njima postoje izvesne razlike, osnovna sintaksa je u su¹tini ista, tako da æemo sintaksu predstavljenu ovde koristiti i sa posix i sa perl kompatibilnim funkcijama. Inaèe, iako su primeri u PHP-u, oni se uz manje izmene mogu prilagoditi i drugim programskim jezicima, jer je sintaksa regularnih izraza manje vi¹e ista.

Sintaksa Regularnih Izraza

Prva dva specijalna karaktera sa kojima æemo poèeti su '^' i '$'. Oni oznaèavaju poèetak, odnosno kraj stringa. Tako na primer:

"^foo" - proverava da li string poèinje sa "foo"
"foo$' - proverava da li se string zavr¹ava sa "foo"

Recimo ako imamo string "Mali Perica uci PHP", reènikom PHP-a to bi izgledalo ovako:

<code>
<?php

$string = "Mali Perica uci PHP";

// Vraca true ako se string "Perica" nalazi u promenljivoj $string
ereg("Perica", $string);

// Vraca true ako $string pocinje sa "Mali"
ereg("^Mali", $string);

// Vraca true ako se $string zavrsava sa "PHP"
ereg("PHP$", $string);

// Vraca true ako string sadrzi tacnu frazu "Mali Perica uci PHP"
ereg("^Mali Perica uci PHP$", $string);
?>
</code>

U kodu gore koristili smo ereg f-ju, koja spada u grupu Posix kompatibilnih f-ja. Obratite pa¾nju da je ona 'case-senzitivna' (za case-insensitive koristite funkciju eregi).

Simboli '?', '+', '*' i {} oznaèavaju broj pojavljivanja nekog karaktera u stringu:

? - Karakter koji prethodi znaku '?' mo¾e se pojaviti jednom ili nijednom

(za pattern "ab?" odgovaralo bi "a","ab")

* - Karakter koji prethodi znaku '*' mo¾e se pojaviti nijednom ili vi¹e puta (za pattern "ab*" odgovaralo bi "a", "ab", "abb", "abbb", ...)

+ - Karakter koji prethodi znaku '+' mo¾e se pojaviti jedanput ili vi¹e puta
(za pattern "ab*" odgovaralo bi "ab", "abb", "abbb", ...)

{n} - Karakter koji prethodi znaku {n} mo¾e se pojaviti taèno n puta
(za pattern "ab{3}" odgovaralo bi "abbb")

{n, } - Karakter koji prethodi znaku {n, } mo¾e se pojaviti najmanje n puta
(za pattern "ab{3,}" odgovaralo bi "abbb", "abbbb", "abbbb", ...)

{n,m} - Karakter koji prethodi znaku {n,m} mo¾e se pojaviti n do m puta.
(za pattern "ab{2,4}" odgovaralo bi "abb", "abbb", "abbbb")

Pored broja pojavljivanja, mozemo definisati i taèan skup znakova koje string sme da sadr¾i. Na primer:

'.' - Bilo koji karakter
[abc] - Samo slova a, b i c
[a-z] - Sva mala slova od a do z
[A-Z] - Sva velika slova od A do Z
[a-zA-z] - Sva slova, mala ili velika
[0-9] - Svi brojevi od 0 - 9
[a-zA-Z0-9] Svi alfanumericki karakteri

Unutar zagrada [] simbol '^' koristimo kao negaciju, tako na primer, ako ¾elimo da na¹ string ne sadr¾i brojeve koristili bi ne¹to poput:
[^0-9].

Pored skupova znakova koje sami defini¹emo, postoje veæ predefinisani skupovi znakova, a to su:

[[:alnum:]]  - Bilo koji alfanumerièki karakter (isto ¹to i [a-zA-Z0-9])
[[:alpha:]]  - Bilo koje slovo (isto ¹to i [a-zA-Z])
[[:upper:]]  - Bilo koje veliko slovo (isto ¹to i [A-Z])
[[:lower:]]  - Bilo koje malo slovo (isto ¹to i [a-z])
[[:blank:]]  - Tab i space karakter (isto ¹to i [\t ])
[[:space:]]  - Bilo koji space karakter
[[:digit:]]  - Bilo koji broj (isto ¹to i [0-9])
[[:xdigit:]] - Bilo koji heksadecimalan broj
[[:punct:]]  - Bilo koji od znakova ".,"'?!;:"
[[:print:]]  - Svi printabilni karakteri
[[:graph:]]  - Svi printabilni karakteri (osim spaceova)

I poslednje ali ne i najmanje bitno, izbor od <string>taèno
jednog</string> elementa iz definisanog skupa:
(string1|string2|...|stringn)
Na primer za pattern "(a|b)cde" stringovi "acde" i "bcde" bi bili
odgovarajuæi. Takoðe, zagrade mo¾emo koristiti za pravljenje
"subpatterna", poput "ba(na)+" ("bana","banana","bananana", ...).

Toliko o teoriji, a sada da vidimo kako regularne izraze upotrebiti u
praksi, za recimo validaciju forme.

<code>
<?php

// validacija korisnickog imena
// dozvoljavamo samo korisnicko ime koje sadrzi alfanum i donju crtu
// minimum 6, max 20 karaktera
$found = preg_match("/^[a-zA-Z0-9_]{6,20}$/", $username);

if(!$found)
{
   echo "Korisnicko ime nije validno";
}

// validacija telefona
// prihvatamo samo brojeve i karaktere iz skupa [+/()]
//obratite paznju na koriscenje escape karaktera "\" za '.', '/', '(' i ')'
$found = preg_match("/^[0-9+\-\.\/\(\) ]{6,30}$/", $phonenum);

if(!$found)
{
   echo "Telefon nije validan";
}

//validacija datuma u mysql formatu (YYYY-MM-DD)
$found = preg_match("/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/", $datum);

if(!$found)
{
   echo "Datum nije validan";
}

// validacija usa zip koda
// USA zipocode je u formatu xxxxx ili xxxxx-xxxx
// gde je x bilo koji ceo broj
// na primer 12345 ili 12345-1234

$found = preg_match("/^([0-9]{5}|([0-9]{5}-[0-9]{4}))$/", $datum);

if(!$found)
{
   echo "Zipcode nije validan";
}

// jednostavna validacija emaila
$found = preg_match("/^[a-zA-Z0-9\.\-]+@[a-zA-Z0-9\.\-]+$/", $datum);

if(!$found)
{
   echo "e-mail nije validan";
}
?>
</code>

Uskoro sledi nastavak, sa obja¹njenjem na praktiènom primeru. Konkretno, biæete u prilici da proèitate case study na temu parsiranja srpskih blogova.

Dragan Diniæ je 32-godi¹nji Web programer sa preko 5 godina iskustva u razvoju LAMP based aplikacija. U slobodno vreme Dragan vodi svoj blog ili diskutuje o Internet tehnologijama na raznim Web forumima.



PHP kurs: Case study - Parsovanje srpskih blogova

Ovaj tekst se nadovezuje na prethodni tekst Dragana Diniæa PHP kurs: Uvod u regularne izraze.

Autor: Dragan Diniæ

Nivo teksta: Srednji

Za kraj æemo odraditi jedan mini projekat, a to je parsovanje liste blogova sa sajta http://planetoid.srpski.org.

Ovaj projekat je ne¹to komplikovaniji nego uobièajeni zadaci te vrste, jer je sajt  planetoid-a na æirilici, ¹to je donekle problem jer se PHP jo¹ uvek ne snalazi dobro sa UTF karakterima.

Uèitavanjem sajta http://planetoid.srpski.org u browser, nakon analize  html source-a dolazimo do formata u kome su sme¹teni podaci unutar
html-a koje trebamo da "isèupamo":

<li><a href="{url_bloga}">{ime_bloga}</a>
<a href="{feed_bloga}">(извор)</a></li>

Nakon kraæe analize, dolazimo do konaènog koda:

<?php
/**
* @version $Id$
* @package dinke.net
* @copyright &copy; 2006 Dinke.net
* @author Dragan Dinic <[email protected]>
*/

//get blogs from planetoid
require_once("curl_http_client.php");

//create new instance of curl class
$curl = &new Curl_HTTP_Client(true);

//and store html data from planetoid into string
$html_source = $curl->fetch_url("http://planetoid.srpski.org/", null, 30);
//echo $html_source;
$found = preg_match_all("/<li><a href=\"([^\"]+)\">([^<]+)<\/a> <a
href=\"([^\"]+)\">\(извор\)<\/a><\/li>/", $html_source, $matches);
//var_dump($matches);
$urls = $matches[1];
$names = $matches[2];

foreach($urls as $key=>$value)
{
  echo "$value\t$names[$key]\n";
}
?>

Kao ¹to vidite, koristili smo Curl  klasu za skidanje html-a sa planetoida. Kljuèna stvar ovde de¹ava  se 19-toj liniji koda, koja sadr¾i regularni izraz koji "èupa" sve podatke o blogovima i sme¹ta ih u $matches niz. Obratite pa¾nju da smo delove patterna koje smo ¾eleli da isèupamo stavili unutar zagrada (za deo koji sadr¾i url bloga i ime bloga), a upravo ti delovi æe biti  prisutni u $matches nizu. Nakon importa, sa podacima mo¾emo da radimo  ¹ta god ¾elimo (¹tampamo na ekran, smestimo u bazu itd.).

Eto toliko o regularnim izrazima za ovaj uvodni tutorijal. Za kraj evo i nekoliko linkova koji vam mogu biti od koristi prilikom daljeg
prouèavanja regularnih izraza.

PHP Regular Expression Functions (POSIX Extended)
Regular Expression Functions (Perl-Compatible)
Wikipedia
Tutorial
Sjajan Ajax based RegExp eValuator

Planetoid

b92