Frequento da poco il forum e notare un paio di post di help sulle espressioni regolari mi ha spinto a scrivere questa pillolinainaina (ci vorrebbe un intero libro) su tale argomento ed in modo specifico sulle 'espressioni regolari perl compatibili'. So che questo è + un argomento di perl che di PHP, ma io conosco poco e niente il perl e le espressoni regolari le ho apprezzate in accoppiata al PHP quindi prego i moderatori di non spostare il post!
Mi scuso con tutti se quanto scrivo non è di semplice lettura ma le espressioni regolari non sono noccioline.
(In questo testo il termine ‘preg’ indica l’espressione regolare, il termine ‘stringa’ indica la sequenza di caratteri su cui stiamo applicando l’espressione regolare cioè la stringa sulla quale cerchiamo o vogliamo sostituire qualcosa)
CONCETTI GENERALI
- 1 –
Le preg sono frutto di una mente diabolica e contorta, sono un insieme di caratteri incomprensibili che vengono usate dai matti per fare ricerche o sostituzioni in una stringa ma, una volta imparate, il demone diventa santo e di raddrizza!!!
- 2 –
Le preg cominciano e terminano con un delimitatore, il più usato è lo slash '/' ma può essere usato qualsiasi altro carattere alfanumerico ad eccezione dello spazio e assimilati (vedere funzione isspace()), consiglio comunque di cambiare delimitatore soltanto se lo slash viene usato all’interno della preg in modo intenso; per farla breve il primo carattere della preg viene assunto come delimitatore e il parser deve trovarne uno identico alla fine, a questa regola fanno eccezioni le parentesi tonda ‘(‘ , quadra ‘[‘ o graffa ’{‘ che sia, in questo caso il delimitatore finale deve essere la parentesi inversa ‘)’,’]’,’}’;
- 2 –
Nella preg, dopo il delimitatore finale ci vanno (opzionali) i modificatori, in pratica dei caratteri che modificano il criterio di ricerca; una preg senza modificatori ha ovviamente una impostazione di default: è binary safe (cioè distingue le maiuscole dalle minuscole) e agisce su una singola riga cioè se la stringa contiene un a capo ‘\n’ questo è considerato come fine della stringa
- 4 –
Nelle preg alcuni caratteri hanno un significato speciale per da qui il nome di metacaratteri, per far perdere il significato speciale al metacarattere bisogna anteporgli uno backslash ‘\’, mi spiego ‘$’ è un metacarattere che indica la fina della stringa ‘\$’ è un semplicissimo dollaro, ovviamente anche lo backslash è un meta carattere per cui una stringa “cane\gatto” la devo scrivere come “cane\\gatto” !
5) Le preg tra doppi apici (“) devono fare i conti con il PHP; supponiamo di voler trovare la seguente sequenza di caratteri “$nome \ $cognome” la preg che devo passare all’angine è “/\$nome \\ \$cognome/” ma anche per il PHP i caratteri slash ‘\’ e dollaro ‘$’ hanno un significato speciale per cui nello script dovrò scrive:
$preg=”/\\\$nome \\\\ \\\$cognome/” oppure $
preg=’/\$nome \\ \$cognome/’
COME RAGIONA L’ENGINE PREG
Queste sono le + importanti regole che l’engine segue nel valutare una preg e nell’effetuare la ricerca o una sostituzione in una stringa. Tali regole hanno un importanza fondamentale, saperle ci fa capire come lavora l’engine e quindi come scrivere al meglio le preg
Regola 1
L’engine valuta una preg da sinistra a destra e genera una serie di ‘azioni di confronto’ che verranno applicate alla stringa secondo la regola (2) la preg è riscontrata integralmente in una stringa solo se tutte le azioni di confronto hanno esito positivo nello stesso ordine in cui appaiono nella preg (ricordate sempre da sinistra a destra)
Regola 2
L’engine scorre la stringa partendo da sinistra e verifica se la preg è soddisfatta integralmente, in caso negativo passa alla posizione successiva (a destra) verificando nuovamente la preg, il ciclo si ripete sino a quando la preg non viene riscontrata oppure si raggiunge la fine della stringa. La prima posizione assunta dall’engine è prima del primo carattere della stringa, la seconda posizione è tra il primo e il secondo carattere, la terza tra il secondo e il terzo carattere e così via sino all’ultima posizione che si trova dopo l’ultimo carattere della stringa; Faccio un esempio: supponiamo di avere una stringa “abc” questa è la sequenza delle posizioni (indicate con un punto ‘.’) assunte dall’engine: “.abc” “a.bc” “ab.c” “abc.” (il puntatore si sposta tra i caratteri).
Regola 3
La preg può contenere atomi e asserzioni. Gli atomi hanno un peso in caratteri, le asserzioni no! Un atomo è un carattere che deve essere riscontrato (cioè deve essere presente) in un preciso punto della stringa, le asserzioni indicano semplicemente il verificarsi di una determinata condizione in quel determinato punto.
Il carattere punto ‘.’ in una preg è un metacarattere, è un atomo ed indica qualsiasi carattere:
“/a./” trova riscontro in “aa”,”ab”,”ac”, ...
Queste alcune asserzioni:
^ inizio stringa o di riga (dipende dal modificatore)
$ fine stringa o di riga (dipende dal modificatore)
\b confine di parola
(?=xxx) posizione in cui l’espressione xxx viene riscontrata
(?!xxx) posizione in cui l’espressione xxx non viene riscontrata
Cerchiamo di chiarire le idee con qualche esempio:
Abbiamo una stringa: “abacad”,
chiediamo all’engine di fare una ricerca su tutta la stringa, cioè di non fermarsi appena la nostra preg risulta verificata ma di continuare sino alla fine ed eventualmente restituire risultati multipli (questa è la differenza tra le funzioni preg_match() e preg_match_all() ).
Analizziamo i risultati di queste preg:
“/ab/”:
come risultato avremo “ab” (un solo riscontro) perché ho chiesto all’engine di trovare i due atomi ‘a’ e ‘b’ in successione, ogni atomo ha peso 1 per cui tutta la preg ha peso 2 e quindi se riscontrata restituisce 2 caratteri.
“/a./:
come risultato avremo “ab”,”ac”,”ad” (3 riscontri) perché ho chiesto all’engine di trovare i due atomi ‘a’ e ‘.’(qualsiasi carattere), in successione, ogni atomo ha peso uno per cui anche in questo caso tutta la preg ha peso 2 e quindi ad ogni riscontro restituisce 2 caratteri.
“/a(?=b)/”:
come risultato avremo “a” (un solo riscontro) perché ho chiesto all’engine di trovare l’atomo ‘a’ seguito dall’asserzione ‘b’, l’atomo ha peso 1 l’asserzione ha peso 0, quindi facendo le somme dei pesi la preg ha peso totale 1 e quindi se riscontrata restituisce 1 carattere.
“/a(?=.)/:
come risultato avremo “a”,”a”,”a” (3 riscontri) perché ho chiesto all’engine di trovare l’atomo ‘a’ seguito dall’asserzione ‘.’ (qualsiasi carattere), l’atomo ha peso 1 l’asserzione ha peso 0, quindi facendo le somme dei pesi la preg ha peso totale 1 e quindi ad ogni riscontro restituisce 1 carattere.
Questo un esempio pratico e spero esplicativo:
Codice PHP:
$str="GialloVerdeBiancoGialloRossoGialloVerdeGialloBlu" ;
$replace1=preg_replace("/Giallo(?=Verde)/","Nero",$str);// sostituisce "Giallo"(atomi G,i,a,l,l,o) solo se seguito da “Verde” (asserzione)
$replace2=preg_replace("/GialloVerde/","Nero",$str);//sostituisce "GialloVerde" (atomi G,i,a,...)
echo "1:$replace1
"; //"NeroVerdeBiancoGialloRossoNeroVerdeGialloBlu"
echo "2:$replace2
"; // "NeroBiancoGialloRossoNeroGialloBlu"
Regola 4
La preg può contenere delle alternative separate da una barra verticale ‘|’; l’engine li esamina da sinistra a destra nell’ordine di apparizione. Quest’ordine di valutazione delle alternative ha un’estrema importanza nelle preg usate per fare una sostituzione. Un esempio in questo script:
Codice PHP:
$str="GialloVerdeBiancoGialloRossoGialloVerdeGialloBlu";
$replace1=preg_replace("/GialloVerde|Giallo/","Nero",$str);
$replace2=preg_replace("/Giallo|GialloVerde/","Nero",$str);
echo "1:$replace1
"; //"NeroBiancoNeroRossoNeroNeroBlu"
echo "2:$replace2
"; // "NeroVerdeBiancoNeroRossoNeroVerdeNeroBlu"
Regola 5
Un atomo può essere seguito da un quantificatore che indica quante volte l’atomo deve essere riscontrato in sequenza nella stringa
Questi alcuni quantificatori
* zero o più volte
+ una o più volte
? zero o una volta
{n,} almeno n volte
{n,m} almeno n volte ma non più di m volte
Alcuni esempi:
/abcx*d/ atomi “abc” seguiti da atomo “x” che si ripete 0 o più volte seguita atomo “d”; quindi ha riscontro in stringhe del tipo: “abcd”,”abcxd”,”abcxxd”, etc...
/abcx{1,2}d/ trova riscontro solo in stringhe che contengono “abcxxd” oppure “abcxd”
I quantificatori hanno un comportamento ‘ingordo’ cioè l’engine tenta di verificare l’atomo prima con il suo massimo di ripetizioni poi se fallisce con il suo massimo-1, poi massimo-2 e così via sino al minimo
Esempio:
/abcx{1,2}d/ tenta prima “abcxxd” e poi “abcxd”
Per forzare un comportamento minimale (l’inverso di ingordo) il quantificatore deve essere seguito da ‘?’
Questi alcuni quantificatori minimali
*? zero o più volte
+? una o più volte
?? zero o una volta
{n,}? almeno n volte
{n,m}? almeno n volte ma non più di m volte
Questo un esempio:
Codice PHP:
$str="abcxxxxd abcxxxd abcxxd abcxd";
$replace1=preg_replace("/abcx{3,4}/","ABC",$str);// ingordo
$replace2=preg_replace("/abcx{3,4}?/","ABC",$str);// minimale
echo "1:$replace1
"; //"ABCd ABCd abcxxd abcxd"
echo "2:$replace2
"; // "ABCxd ABCd abcxxd abcxd"
I quantificatori in pratica generano delle alternative che vengono valutate in base alla regola 4 infatti:
“/abcx{1,2}/” si puo scrivere come “/abcxx|abcx/” e
“/abcx{1,2}?/” come “/abcx|abcxx/”;
Regola 6
Le parentesi tonde “(...)” sono metacaratteri e vengono usate per raggruppare una serie di atomi in un unico quantificatore oppure per raggruppare una serie di alternative
Esempio:
“/a(bc){1,2}/” vuol dire “a” seguito da “bc” che si ripete una o più volte quindi questa preg trova riscontro in stringe che contengono “abcbc” oppure “abc”;
“/a(bc|de)/” trova riscontro in stringhe che contengono “abc” oppure “ade”.
continua...