Posts Tagged regular expressions

REGULAR EXPRESSIONS (REGEX)

Regular expressions sunt expresii care ne permit sa specificam formatul unui sir de caractere. Scenariile in care apare nevoia de regex-uri sunt in general doua:  

  • cand dorim sa cautam si sa extragem siruri de caractere cu format cunoscut dintr-un text mai mare. Exemplu: intentionam sa obtinem doar adresele de e-mail dintr-un fisier text care contine diverse informatii despre o lista de utilizatori.
  • cand dorim sa validam date (sa ne asiguram ca anumite informatii au format corect). Exemplu: datele introduse de catre utilizator intr-un formular HTML trebuie sa respecte anumite cerinte – datele calendaristice sa fie formatate intr-un anume fel, numele de persoane sa contina nume si prenume separate prin spatiu sau -, etc.

Exista deosebiri fundamental intre cautarea obisnuita si cea folosind regex-uri:  

  • in cautarea obisnuita, cunoastem de la bun inceput sirul de caractere ce va fi gasit, iar ceea ce ne intereseaza sunt informatii suplimentare (pozitia sau pozitiile aparitiei lui, frecventa de aparitie etc)
  • in cautarea cu regex-uri, nu stim dinainte sirurile de caractere pe care le vom gasi, ci vom obtine toate sirurile corespunzatoare formatului specificat. Asadar scopul cautarii, spre deosebire de cautarea obisnuita, poate fi chiar lista de siruri al caror format   este precizat in regex
                PCRE (Perl-compatible Regular Expressions)

     

Un regex este format dintr-o succesiune de caractere (litere, cifre, semne de punctuatie), insa cu urmatoarele particularitati:  

•     intreaga expresie este cuprinsa intre doua caractere delimitatoare. Caracterul delimitator de inceput este acelasi cu eel
de sfarsit si este ales de catre programator. Acest caracter nu are voie sa fie alfanumeric sau \.  

/regex/ – modalitatea traditional a de delimitare a unui 
#regex# – modalitate alternativa de delimitare 
\regex\ – modalitate invalida  

Nota: daca in interiorul regex-ului estefolosit caracterul delimitator, el trebuie precedat de un \.  

constructia cu paranteze drepte [ ] – tine locul unui singur caracter. Ne permite sa specificam un set discret de caractere ce se pot afla pe o anumita pozitie din sir. Intre paranteze, caracterul ^ (accent circumflex) are rol de negare. Exemple:

Regex Siruri ce corespund Comentarii, explicatii
[flp]in fin, lin, pin constructia [flp] corespunde unui singur caracter, si anume unuia dintre cele specificate in interiorul parantezelor
[r-t]ara rara, sara,tara constructia [r-t] corespunde unui singur caracter, mai exact unuia dintre cele aflate intre r si t (r,s,t)
[br-tv] bara, rara, sara, tara, vara pot fi combinate primele doua modalitati. Indiferent de cate caractere se afla inauntrul parantezelor, constructia tine loc de un singur caracter (in acest caz, acela poate fi b,r,s,t sau v)
[A-Z][a-z] orice cuvant de doua litere care incepe cu litera mare constructia [] poate fi folosita de oricate ori este nevoie in cadrul unui regex
[A-Z][^a-z] orice sir de doua litere care incepe cu litera mare si are pe pozitia a doua orice altceva decat litera mica (ex: C#, A4 etc) [^a-z] corespunde unui singur caracter, care nu poate fi litera mica. Atentie! Negarea unei litere sau a unui set de litere inseamna ca pe pozitia respectiva se pot gasi cifre, semne de punctuatie etc

   

•     punctul – tine locul unui singur caracter. Indica faptul ca pe respectiva pozitie din sir se poate gasi orice caracter (insa unui singur!) cu exceptia newline (\n). Daca este activata optiunea DOT-ALL (vezi mai jos modificatori), punctul corespunde si caracterelor newline.

Regex Siruri ce corespund Comentarii, explicatii
.in fin, lin, pin dar si #in, %in etc . inseamna orice caracter, inclusiv semne de punctuatie, cifre etc
[r-t]a.a rara, sara, tara dar si rata, raba, tata, ta(a, sa@a etc punctul poate fi combinat cu una dintre celelalte constructii
[A-Z]. o litera mare urmata de orice caracter (ex: Am, Nu, F&, H* etc)  

Nota: pentru a specifica chiar caracterul punct pe una dintre poziliile sirului cautat, este necesara precedarea lui cu  

•    clase de caractere uzuale, predefinite. lata cateva exemple:  

o  \d — digit (cifra zecimala) 
o  \D — non-digit (un caracter care nu este cifra zecimala) 
o   \s – whitespace. Corespunde caracterelor spatiu, tab (\t) si newline (\n) 
o   \S – non-whitespace 
o   \w – word character (litera, cifra sau underscore) 
o   \W – non-word character

Regex Siruri ce corespund Comentarii, explicatii
\d-\d unscor la fotbal: 3-2, 1-0 etc constructia [flp] corespunde unui singur caracter, si anume unuia dintre cele specificate in interiorul parantezelor
B\s\d\d\s[A-Z][A-Z][A-Z] un numar de automobil de Bucuresti (ex: B 13 RTG) caracterul B, spatiu, doua cifre, spatiu, 3 litere mari. De remarcat insa ca \s corespunde si cu TAB sau NEWLINE, asadar aceasta expresie nu ar selecta numai numere de masina, ci si succesiuni de 3 linii care contin B pe linia 1, doua cifre pe linia 2 si 3 litere pe linia 3

   

Putem specifica repetitia controlata a unui caracter sau a unei intregi subexpresii folosind doua constructii: 

 •     constructia cu acolade:  

caracter{min,max} – caracterul   se  repeta intre min si  max ori
(regex){min,max} – intreaga expresie se repeta intre min si  max ori  

Sunt permise varialiuni ale acestei sintaxe:  

(regex){n}  -  repetitie de exact n ori 
(regex){min,} -  repetitie de minim n ori,   fara limita superioara a numarului  de  repetitii  

Observatie: atunci cand dorim sa punem conditia de repetare a unei parti a regexului ce contine mai multe caractere, acea subexpresie trebuie inclusa intre paranteze rotunde.  

Exemple:

Regex Siruri ce corespund Comentarii, explicatii
07\d{8} un numar de mobil (ex: 0720123456) caracterele 0 si 7 urmate de 8 cifre
[A-Z]{2}\d{6} serie si numar de buletin (ex: VF735245) o litera mare care se repeta de exact 2 ori, urmata de o cifra care se repeta de exact 6 ori
[A-Z][a-z]{0,1} un element chimic (ex: H, Na etc) o litera mare, urmata de o litera mica ce apare o data sau deloc
[A-Z]{1,2}\s\d{2}\s[A-Z]{3} un numar de automobil (ex: B 35 EDX sau MM 67 WSL) una sau doua litere mari, un spatiu, doua cifre, un spatiu si apoi trei litere mari
[A-Z][a-z]{1,} un cuvant care incepe cu litera mare (ex: Marius)  

metacaractere folosite pentru cazuri de repetitii particulare  

(regex)+ – echivalent cu   (regex){1,}   (minim o repetitie a lui   regex) 
(regex)*   -   echivalent  cu   (regex){0,}   (0  sau  mai   multe   repetitii   ale  lui   regex)
(regex)? – echivalent cu   (regex){0,1}   (regex apare o data sau deloc)  

Exemple:

Regex Siruri ce corespund Comentarii, explicatii
[A-Z][a-z\s]+\. o propozitie (ex: Am un mar.) o litera mare urmata de una sau mai multe litere mici sau spatii si terminandu-se cu un punct
[a-z]+\.[a-z]+@[a-z]\.[a-z]{2-4} o adresa de mail de forma victor.manu@gmail.com o litera mica care se repeta minim o data, un caracter. (observati \-ul), o litera mica care se repeta cel putin o data, caracterul @, din nou o succesiune de litere mici (eel putin una) apoi punct si numele domeniului radacina (com, net, org, info, tv etc)

• caracterul^ – daca apare la inceputul regex-ului, pune conditia ca sirul de caractere gasit sa se afle la inceputul textul ui in care se face cautarea
• caracterul $ – daca apare la sfarsitul regex-ului, impune ca sirul de caractere ce corespunde regex-ului sa se afle la finalul textului in care se face cautarea
Observatie: atunci cand ^ apare in interiorul parantezelor drepte (la specificarea unci clase de caractere), el are alta semnificatie: cea de negare.
Daca in regex se activeaza optiunea MULTILINE (vezi mai jos modificatori), atunci ^ va corespunde fiecarui inceput de linie, iar $ fiecarui sfarsit de linie, daca textul in care se face cautarea este unul de mai multe linii. Optiunea MULTILINE este initial dezactivata, conditii in care ^ corespunde inceputului intregului text iar $ sfarsitului aceluiasi text.
Exemplu: cautand sirurile de caractere ce corespund regex-ului ninge$ pe textul urmator: 

Ziua ninge
noaptea ninge
dimineata ninge iara
 

vor fi gasite urmatoarele siruri, in functie de caz:
daca optiunea MULTILINE este activata, va fi gasit sirul ninge de doua ori – la finalul primei linii si al celei de-a doua. Cuvantul ninge de pe linia 3 nu este gasit deoarece nu se afla la sfarsit de linie
daca optiunea MULTILINE este dezactivata, nu va fi gasit nici un sir, deoarece $ corespunde finalului intregului text, iar textul nu se termina cu sirul ninge . 

 Modificatori de optiuni 

Regex-ul poate contine si elemente care nu desemneaza caractere, clase de caractere sau repetitii, ci specifica optiuni aplicate motorului de regular expressions in cazul expresiei respective. Modificatorii se pot specifica cu urmatoarea constructie in interiorul unui regex: 

(?optiuni) – activarea uneia sau mai multor optiuni
(?-optiuni) – dezactivarea uneia sau mai multor optiuni
(?optiuni1-optiuni2) – activarea optiunilor din grupul 1 si dezactivarea celor din grupul 2 

Iata cateva optiuni uzuale:
• CASELESS (i) – in aplicarea regex-ului in cauza nu se va mai face distinctie intre literele mici si mari
• DOT-ALL (s) – punctul va corespunde oricarui caracter, inclusiv newline
• MULTILINE (m) – caracterele ^ si $ nu vor mai corespunde doar inceputului si sfarsitului intregului text in care se cauta, ci fiecarui inceput si sfarsit de linie componenta
Exemple:

Regex Siruri ce corespund Comentarii, explicatii
(?i)php php, PHP optiune utila atunci cand cautam o anumita succesiune de litere, indiferent daca sunt mici sau mari; fara aceasta optiune am fi fost fortati sa scriem [Pp][Hh][Pp]
(?im)php$ php sau PHP aflate la sfarsit de linie pot fi activate sau dezactivate mai multe optiuni simultan
ab(?i)cd(?-i)ef abedef, abCDef, abCdef, abcDef optiunile pot fi activate numai pe o portiune a regex-ului (in cazul nostru, CASELESS este activ numai pentru literele c si d)
 

In cadrul unui regex, caracterul special | permite specificarea unor formate alternative pentru sirul de caractere cautat/validat sau pentru o portiune a sa: 

regexl   |   regex2                       caracterul are rol de sau 

 Exemple:

   

Regex  Siruri ce corespund  Comentarii, explicatii 
Mari(e|oara)  Marie, Marioara  finalul sirului cautat are doua formate posibile 
(021|07\d)\d{7}  un numar de Bucuresti (fix) sau de mobil  021 urmat de 7 cifre, sau 07 urmat de 8 cifre 
([fs]|vs|)printf  suita de functii printf din PHP (printf, fprintf, sprintf, vsprintf)  sirul incepe cu f, cu s, cu vs sau cu nimic (remarcati caracterul | de dinante de paranteza rotunda inchisa) urmat de printf’. 
(f|v?s|)printf  inaintea lui printf se poate afla: nimic, caracterul f, sau caracterul s precedat sau nu de un v 

Sub-expresii 

Sub-expresiile reprezinta portiuni ale unui regex delimitate prin paranteze rotunde si care pot fi extrase separat sau referite chiar din cadrul regex-ului. Ele sunt utile in doua cazuri: 

  • cand cautam un sir de caractere corespunzator unui regex insa ne intereseaza numai o parte a sa, care nu putea fi cautata independent (un subsir). Exemplu: dorim sa extragem toate link-urile dintr-un fisier HTML; pentru aceasta cautam toate sirurile de fonna <a href=….>…</a> si extragem din ele numai valoarea atributului href
  • cand dorim ca, din cadrul unui regex, sa ne referim la o portiune anterioara a sa (“back references”). Exemple: a) un regex! incepe cu un caracter (ales de catre programator, asadar necunoscut) dar se termina cu acelasi caracter. b) un tag HTML, care are delimitatori de deschidere si de inchidere, ultimul putand fi specificat prin referire la primul

Subexpresiile primesc automat numere incepand de la 1, in ordinea in care apar in regex, pentru a oferi posibilitatea referirii lor din cadrul regex-ului si a extragerii sirurilor de caractere ce le corespund. Daca se doreste referirca unei subexpresii din cadrul regex-ului, se pot folosi secventele \1, \2 …\99 ce semnifica sirul de caractere ce a corespuns primei subexpresii, celei de-a doua subexpresii etc 

Exemplu: aplicandu-se regex-ul ^([A-ZJ+) cel ([a-z]+)$ pentru sirurile de caractere din prima coloana a tabelului de mai jos, continuturile subexpresiilor sunt cele din coloanele 2 si 3:

  Subexpresia 1([A-Z\s]+) Subexpresia 2 ([a-z]+)
“Andrii Popa cel voinic” Andrii Popa voinic
“Stefan cel mare” Stefan mare
“Mircea cel batran” Mircea batran
  Exemplu: <(?i)([A-Z]+)>[^<]*</ \1> 
sirul incepe cu <, se activeaza optiunea CASELESS, continua cu una sau
(marl sau mici    constituie prima subexpresie, Urmeaza >,   apoi 0 sau
diferite de <, si tagul de inchidere, format, din </ si apoi sirul ce a
subexpresii

Exista cazuri in care dorim sa includem o portiune a regex-ului intre paranteze, insa fara ca ea sa fie automat considerata (si numerotata) ca sub-expresie (ex: (021 |07\d)\d{7}, unde parantezele sunt folosite numai pentru specificarea formatului alternativ al inceputului de sir). In astfel de cazuri putem folosi secventa (?: ) pentru a incadra portiunea de regex dorita; intre ? si : pot fi specificate si optiuni 

   Functii PHP predefinite pentru lucrul cu regex-uri 


Iata principalele functii PHP predefinite pentru lucrul cu PCRE:
•     int preg_match ( string $regex, string $string [, array &$matches [, int $flags [, int $offset]]]) – cauta in $string prima aparitie a unui sir ce corespunde formatului specificat in $regex. Returneaza 1 in caz de gasire si 0 in caz contrar. Daca este folosit si al treilea argument, acesta se populeaza astfel: $matches[0] contine intregul sir ce a corespuns regex-ului, $matches[l] sirul corespunzator primei subexpresii etc. Al patrulea argument, daca exista, ofera posibilitatea de a impune pozitia din $string de la care sa inceapa cautarea.
  

$s =    “mere pere in panere”  ;
echo preg_match(‘/ere/’,   $s);
$a = array() ;
preg_match(‘/(.)ere\s(.)/’,$s, $a);
var_dump($a);  
array(2)  {
[0]=>strings(4) “mere p”
[1]=>string(1) “m”
[2]=> string(1) “p”
}
  

int preg_match_all ( string $regex, string $string, array &$matches [, int $flags [, int $offset]]) – functioneaza la fel ca preg_match, insa al treilea argument este obligatoriu, el fiind populat cu toate sirurile gasite care corespund pattern-ului (preg_match se oprea la prima aparitie). Felul in care este populat acest tablou este dat de parametrul $flags, care ofera urmatoarele posibilitati: 

• PREG_PATTERN_ORDER – elementele din $matches corespund subexpresiilor din $regex. $matches[0] va avea ca valoare un tablou cu toate sirurile intregi care au corespuns regex-ului, $matches[l] va avea ca valoare un tablou ce contine toate sirurile ce au corespuns primei subexpresii etc.
 PREGSETORDER – fiecare element din $matches corespunde unei aparitii de subsir ce corespunde regex-ului, si are ca valoare un tablou cu toate sirurile corespunzatoare subexpresiilor. Smatches[0][0] contine 

  • mixed preg_replace ( mixed $regex, mixed $inlocuitor, mixed $string [, int $limit [, int &$count]]) – returneaza o copie a lui $string in care subsirurile ce corespund formatului din $regex sunt inlocuite cu $inlocuitor. Al patrulea argument, daca este prezent, specifica numarul maxim de inlocuiri, iar in ultimul argument se memoreaza numarul de inlocuiri efectuate.
  • array preg_split ( string $pattern, string $subject [, int $limit [, int $flags]]) – alternativa la strtok() sau explode(), cu diferenta ca delimitatorul de campuri (sirul de caractere declarat ca separator) este acum specificat sub forma uni regex

[catlist id=18 numberposts=10]

, ,

No Comments