Follow me on Twitter Facebook Flickr Subscribe Feeds
 

PHP: usare le regular expression – parte 2

This entry was posted on August 28th, 2009 and is filed under php.

Eccoci alla seconda parte dell’articolo: PHP: usare le regular expression.

La scorsa volta abbiamo definito cos’è una espressione regolare, ovvero un insieme di regole con cui è possibile rappresentare una stringa, e la sintassi da essa utilizzata per descrivere tali regole. Per comodità elenco anche in questo articolo la sintassi e i metadati che utilizzano le RE:

^ Inizio stringa
$ Fine stringa
[a-z] Ogni lettera compresa tra a e z in minuscolo
[A-Z] Ogni lettera compresa tra A e Z in maiuscolo
[0-9] Numeri da 0 a 9
[^0-9] Escludi i numeri da 0 a 9
[^A-G] Escludi i caratteri in maiuscolo tra A e G
? Zero o una ripetizione dell’espressione che lo precede
* Zero o più ripetizioni dell’espressione che lo precede
+ Una o più ripetizioni dell’espressione che lo precede
{2} Esattamente due ripetizioni dell’espressione che lo precede
{2,} Due o più ripetizioni dell’espressione che lo precede
{2,4} Tra due e quattro ripetizioni dell’espressione che lo precede
. Ogni carattere
(a|b) a OR b
\s Spazio vuoto

\d = [0-9] (digit)
\D = [^0-9] (digit esclusi)
\w = [0-9A-Za-z] (tutti i caratteri esclusi i simboli)
\W = [^0-9A-Za-z] (solo simboli)
\s = [ \t\n\r] (caratteri di spaziatura)
\S = [^ \t\n\r] (esclusi caratteri di spaziatura)
i = ignora case sensitive
m = modalità multilinea
u = pattern in UTF-8

Abbiamo anche stabilito che i set di funzioni che il PHP offre sono 3:

  1. preg Function Set
  2. eregi Funciont Set
  3. mb_eregi Function Set

Del primo set ne abbiamo discusso la scorsa volta e, tra le funzioni mostrate, le più importanti sono due:

  • preg_match($pattern, $subject) che restituisce vero o falso a seconda che il pattern sia o meno nella stringa
  • preg_replace($pattern, $replacement, $subject) che restituisce una stringa, o un array di stringhe, con le sostituzioni identificate dal pattern.

eregi Function Set

Se il set preg usava la sintassi Perl-compatible, il set eregi si basa invece sulla sintassi POSIX compatible. Parliamo comunque di funzioni che, a partire dalla versione 5.3.0 di PHP sono deprecate e dalla 6 addirittura eliminate. Bisogna quindi fare molta attenzione all’uso che se ne fa e la destinazione a cui saranno soggetti gli script in cui esse verranno utilizzate.

Le funzione incluse nel set sono le seguenti:

  1. ereg_replace
  2. ereg
  3. eregi_replace
  4. eregi
  5. split
  6. spliti
  7. sql_regcase

Analizziamole tutte nel dettaglio. Dato che stavolta veniamo da una base (seppur minima) del set precedente, procederò per ordine nell’analisi. Una nota prima di iniziare; se nel primo set l’ordine di visualizzazione delle espressioni regolari era delimitato con il simbolo del dollaro ($1, $2, …), in questo caso l’ordine di visualizzazione è indicato con il simbolo \\ (\\1, \\2, …). Con \\0 invece ci si riferisce all’intera stringa.


1. ereg_replace()

Questa funzione è l’equivalente di preg_replace, sostituisce quindi la regex con una espressione scelta dal programmatore. Il suo prototipo è il seguente:

string ereg_replace ( string $pattern , string $replacement , string $string )

Il funzionamento (seppur limitato rispetto alla precedente) è il medesimo, quindi il primo parametro prende in ingresso la regular expression, il secondo il rimpiazzo e l’ultimo la stringa su cui operare. Un esempio di funzionamento è il seguente:


$pattern = "^(.*)";
$replacement = "<strong>\\0</strong>";
$string = "Lorem ipsum dolor sit amet";
echo ereg_replace($pattern,$replacement,$string);

Il codice di sopra altro non fa che convertire la stringa $stringa in grassetto.


1. ereg()

Questa funzione è l’equivalente di preg_match, verifica che sia presente la regex all’interno della stringa. Il suo prototipo è il seguente:

int ereg  ( string $pattern  , string $string  [, array &$regs  ] )

Verifica quindi che l’espressione contenuta in $pattern sia presente dentro $string. Un semplice esempio per chiarire eventuali dubbi è il seguente:


echo ereg("^[a-z0-9_-]{3,16}$", "username");
echo ereg("^[a-z0-9_-]{3,16}$", "Username 2");

La regular expression di sopra verifica che la stringa passata sia composta da una sola parola di lunghezza compresa tre 3 e 16 caratteri, composta di soli caratteri minuscoli, cifre e i simboli “_” e “-”. Una semplice espressione per verificare la correttezza di uno username. Nel primo caso restituirà 1, nel secondo non restituirà nulla perchè sono presenti due errori.


3. eregi_replace – 4. eregi()

Queste due funzioni sono identiche a quelle di sopra con l’unica differenza che non fanno distinzione dei caratteri minuscoli e maiuscoli.

Un semplicissimo esempio chiarirà le idee:


echo eregi('v', "ABCDVF");

Tale funzione restituirà 1 (true) perchè anche se la lettera V presente è maiuscola, la funzione non fa distinzioni tra i due casi.


5. split() – 6.spliti()

La funzione split (o spliti) suddivide una stringa in un array in base ad una regular expression. Il suo prototipo è il seguente:

array split  ( string $pattern  , string $string  [, int $limit  ] )

Restituisce un array contenente le stringhe che fanno match con il pattern. Il parametro facoltativo $limit viene utilizzato se si vuole fermarsi dopo un determinato numero di occorrenze. Un esempio preso dal sito di PHP.net che è utile anche per capire il funzionamento della “i” al termine delle funzioni elencate (spliti, eregi,…):


$string = "aBBBaCCCADDDaEEEaGGGA";
$chunks = spliti ("a", $string, 5);
print_r($chunks);

Se non siamo stati attenti nel leggere il codice, ci aspetteremmo un array contenente i seguenti risultati { “”, “BBB”,”CCCADDD”,”EEE”,”GGGA”}. Dato però che utilizziamo la spliti() terremo conto anche del carattere A maiuscolo e quindi l’array risultante sarà ben diverso da quello che ci saremmo aspettati.


7. sql_regcase()

L’ultima funzione del set potrebbe sembrare curiosa. Essa restituisce una regular expression case insensitive che fa match con l’input ricevuto. Un esempio chiarirà sicuramente meglio della definizione:


echo sql_regcase("Prova");

Tale chiamata della funzione restituirà la seguente stringa: “[Pp][Rr][Oo][Vv][Aa]” che effettivamente fa match con la stringa in input.


mb_ereg Function Set

L’ultimo set ha il medesimo funzionamento del set ereg con una differenza sostanziale. Mentre il set ereg tratta le stringhe come un insieme di caratteri di 8bit, il set mb_ereg lavora con il supporto multi-byte delle stringhe.

La pagina di riferimento per tale set di funzioni è locata al seguente indirizzo:

http://us3.php.net/manual/en/ref.mbstring.php


Con l’ultimo set (solo enunciato) finisce la carrellata di funzioni che il PHP offre per la gestione delle regular expression. Arrivati a questo punto, abbiamo capito come è strutturata un’espressione regolare, come possiamo gestirla e manipolarla. All’inizio sembrano qualcosa di impossibile da comprendere per molti e spesso ci vien voglia di mandarle a quel paese e desistere. Bisogna però solo entrare nella loro ottica e comprendere semplicemente la logica su cui sono fondate.

Giunti a questo punto però è arrivato il momento di utilizzarle realmente e non lasciare i semplici esempi inseriti nei due articoli. In quest’ultima parte mi occuperò di inserire alcuni esempi reali di utilizzo delle espressioni e di fornire alcuni link di approfondimento per migliorare sempre più l’esperienza.

Espressioni Regolari comuni

  • Regular Expression Email
    $pat = "^[a-z\'0-9]+([._-][a-z\'0-9]+)*@([a-z0-9]+([._-][a-z0-9]+))+$";
  • Regular Expression Dominio
    $pat = '/^(http(s?):\/\/{1})((\w+\.){1,})\w{2,}(\/?)$/i';
  • Regular Expression Username
    $pat = "^[a-z0-9_-]{3,16}$";
  • Regular Expression Password
    $pat = "/^[\S]{6,18}$/";
  • Regular Expression Data
    $pat = "^[0-9]{2}/[0-9]{2}/[0-9]{4}$";

Direi che già avere a disposizione questo piccolissimo set di regex ci consente di analizzare l’input di una comune form senza grossi problemi. Possono sicuramente essere un’ottima base di partenza per espressioni molto più elaborate.

Riferimenti Esterni

In questa sezione invece, prima di chiudere l’articolo, vi elenco una serie di articoli riguardanti l’argomento:

Per qualunque chiarimento, precisazione, potete tranquillamente contattarmi. Sarò felice di aiutarvi! :)

Like this post? Share It! :)
Navigation:
Related Posts:
Comments

21 Responses to “PHP: usare le regular expression – parte 2”

  1. giovanni
    October 10, 2009 at 8:01 am

    ciao!
    senti, ti chiedo una mano visto che vedo che ci capisci sulle regex :D
    io devo ottenere (per il mod rewrite) tutti i pezzi di questo indirizzo
    locali/roma/2-butteri-2.html
    dove
    locali è fisso
    roma è la città, prima variabile
    2 è l’id del locale
    butteri è il nome
    2 finale è la pagina relativa al locale da aprire.

    per l’url
    locali/roma/2-butteri.html
    uso la reges
    RewriteRule ^locali/(.*)/([0-9]+)-(.*)\.html$ locale.php?citta=$1&id_locale=$2&rew=$3 [L]

    e funziona bene, ma quando vado a mettere

    RewriteRule ^locali/(.*)/([0-9]+)-(.*)-([0-9]+)\.html$ locale.php?citta=$1&id_locale=$2&rew=$3&page=$4 [L]

    mi da problemi, in quanto (.*) mi prende pure -2!
    come posso dirgli tutti i caratteri escluso il -?

    grazie mille!

  2. Simone D'Amico ← http://www.simonedamico.it
    October 10, 2009 at 11:58 am

    Ciao Giovanni,
    innanzitutto benvenuto sul blog! :)

    Venendo al tuo problema ho notato che “butteri” nel tuo caso è testuale, e da come hai scritto è un nome. Se non ci sono caratteri alfanumerici oltre a numeri e caratteri potresti usare questo token:

    [A-Za-z0-9]

    o i similari che escludano altri tipi di caratteri oltre questi. Se invece c’è uno o piu carattere che possono dar fastidio oltre il “-” puoi usare il simbolo di esclusione “^” indicando tra parentesi quadre i singoli caratteri che non vuoi. Es:

    [^a-z]

    Se hai bisogno di ulteriori spiegazioni son qui! :)

    Ciao

  3. giovanni
    October 10, 2009 at 12:05 pm

    Magari! ti riscrivo subito :D
    ho il testo butteri potrebbe essere ristorante_butteri o casa_mia_e_molto_bella :D
    cioè ho _ al posto degli spazi

    ho provato sia con
    ([^-]+)
    che con
    [A-Za-z0-9-]
    al posto di (.*) (sto provando da ieri :D ), ma non so perchè non mi funzionano :(

    grazie mille!!!

  4. Simone D'Amico ← http://www.simonedamico.it
    October 10, 2009 at 12:17 pm

    Devi stare attento al carattere “-” in quanto essendo un operatore dovresti inserirlo tra apici per farlo trattare come carattere.

    Comunque io ho appena provato la tua regex in locale con il link che mi hai dato:

    locali/roma/2-butteri-2.html

    e non ho visto problemi. Se faccio la print_r dell’array GET ho questo risultato:

    Array
    (
    [citta] => roma
    [id_locale] => 2
    [rew] => butteri
    [page] => 2
    )

    EDIT:
    io nell’.htaccess ho anche queste direttive però:

    Options +FollowSymLinks
    RewriteEngine on

  5. Firenze_Alessio
    December 22, 2009 at 4:42 am

    Ciao, prima di tutto complimenti per l’articolo. Avrei una domanda, visto che di espressioni regolari te ne intendi. Devo sostituire una parola che non sia compresa in un tag A e che non sia neanche un attributo di un tag. Mi spiego meglio, ogni parola “Italia” che non è compresa in un link, quindi Italia non va bene e che non è in un attributo di un altro qualsiasi tag, per esempio non andrebbe ancora bene, deve essere sostituita con “Italia Bella” per fare un esempio. Riesco ad individuare il tag A e quindi a non far matchare l’espressione regolare con questa regex:
    @.*?@si
    Per il resto sono in alto mare!
    Questa cosa potrebbe tornare utile a moltissime persone che hanno la necessità di processare del contenuto html e sostituire delle parole solo in determinati punti. Senza l’uso dell’espressioni regolari, penso che uno script php impegherebbe 5 minuti a fare questo lavoro, per esempio usando la funzione strpos() e processando ogni posizione con una substr(). Ti ringrazio in anticipo!

  6. Matteo Marchigiani
    February 28, 2010 at 11:26 pm

    Per prima cosa complimenti per le guide, sono davvero ottime, utilissime! ;)
    Detto questo ti chiedo se sia possibile, usando appunto un regex, fare il parsing di una pagina web. In effetti so che è possibile, ma mi spiego meglio: vorrei estrarre (per inserire in un db) tutto il contenuto di un div che a sua volta ne comprende altri; il problema è che ogni volta che ci provo il preg_match mi ritorna solo il contenuto del primo div all’interno del div principale di cui voglio estrarne l’intero contenuto. :(
    Che posso fare? E’ mica un limite dei regex???

    Grazie. Ciao! :)

  7. Simone D'Amico ← http://www.simonedamico.it
    March 1, 2010 at 12:11 am

    Ciao Matteo,
    benvenuto sul blog!

    Secondo me quando si ha a che fare con il parsing di codice HTML è più indicato utilizzare librerie ad hoc che fanno proprio quello.

    Prima di ammattire con le regex, perchè non provi ad esempio con librerie che gestiscono il DOM della pagina?

    Un esempio potrebbe essere: http://simplehtmldom.sourceforge.net/

    ma ce ne sono tanti altri in giro.

  8. Matteo Marchigiani
    March 2, 2010 at 1:12 am

    Il fatto è che le avevo già usate per estrarre il contenuto di diversi div singoli, e pensavo che si potessero usare anche per parsare un div che a sua volta ne contiene altri… speravo vi fosse un’espressione particolare, sai. :)
    Quindi mi confermi che l’unico modo è optare per delle librerie come quella consigliata? Grazie di nuovo. Ciao.

  9. Simone D'Amico ← http://www.simonedamico.it
    March 2, 2010 at 11:15 am

    Non è che è l’unica soluzione ma sicuramente la migliore per questo genere di cose. Per sua natura il contenuto di una pagina web è dinamico quindi affidarsi ad una regular expression a lungo andare potrebbe rivelarsi dannoso e controproducente.

    Il DOM invece concettualmente resta lo stesso: tag annidati. Secondo me è la scelta migliore, ma è solo il mio punto di vista! ;)

  10. Marco
    April 26, 2010 at 7:04 pm

    Ciao Simone!
    Sono interessato a questo pezzo di codice:

    $pattern = “^(.*)”;
    $replacement = “\“;
    $string = “Lorem ipsum dolor sit amet”;
    echo ereg_replace($pattern,$replacement,$string);

    Però vorrei diventassero in grassetto i primi n caratteri della stringa…puoi darmi un aiuto?
    Grazie!

  11. Simone D'Amico ← http://simonedamico.com
    April 26, 2010 at 9:34 pm

    Ciao Marco,
    innanzitutto ti consiglio di usare il set di funzioni della prima parte perchè le ereg sono state deprecate.

    Comunque potresti fare una cosa molto semplice. Tagli la stringa iniziale ad n, la passi in grassetto e poi la re-incolli alla stringa. Semplice e veloce!

  12. Marco
    April 27, 2010 at 12:58 pm

    Grazie Simone del consiglio!
    Ho risolto il mio problema…un’ultima cosa sulla RE:

    preg_replace(“/^(\w+.*)/”,”$1“,$stringa);

    Ho aggiunto “.*” per far riconoscere anche gli spazi ma rimangono fuori però i seguenti caratteri accentati tipo: è,ò,à,etc…
    Come si può modificare la RE in modo che riconosca anche questi? Grazie ancora!

  13. Simone D'Amico ← http://simonedamico.com
    April 27, 2010 at 10:32 pm

    I caratteri accentati non andrebbero mai usati così come sono ma andrebbero “trasportati” e gestiti attraverso la loro codifica in HTML. Dovresti quindi gestire la regex in maniera che accetti caratteri del tipo:

    & egrave;
    & agrave;

    Questo perchè codifiche o charset diversi potrebbero causarti non pochi problemi!

  14. Marco
    April 28, 2010 at 10:08 am

    Quindi come andrebbe modificata la mia RE “/^(\w+.*)/”?

  15. Simone D'Amico ← http://simonedamico.com
    April 28, 2010 at 7:06 pm

    Prima di modificare la regex devi fare in modo che la stringa ad essa passata contenga già i caratteri accentati codificati, ad esempio con htmlentities (http://php.net/manual/en/function.htmlentities.php) o con funzioni simili.

    Una volta fatto ciò non devi fare altro che dirgli di accettare anche i caratteri alfanumerici. Modifica lo \w che non li accetta sostituendolo con il set di caratteri che puoi accettare, ad esempio \D se non vuoi i numeri.

  16. Angelo
    May 28, 2010 at 8:03 pm

    “Una volta fatto ciò non devi fare altro che dirgli di accettare anche i caratteri alfanumerici. Modifica lo \w che non li accetta sostituendolo con il set di caratteri che puoi accettare, ad esempio \D se non vuoi i numeri.”

    Ciao.. non capisco come si dovrebbe fare a far accettare le accentate.
    Che significa: “Modifica lo \w che non li accetta sostituendolo con il set di caratteri che puoi accettare, ad esempio \D se non vuoi i numeri.”
    Cioé che ci devo mettere nella regex per fargli accettare le accentate?

  17. Simone D'Amico ← http://simonedamico.com
    May 28, 2010 at 9:24 pm

    Ciao Angelo, leggi la prima parte del commento a cui ti riferisci.

    In pratica io sconsiglio vivamente di trattare le lettere accentate perchè spesso si perdono per strada, soprattutto quando si ha a che fare con charset differenti tra db e linguaggio server.

    Il mio consiglio quindi è:
    1. trasformi il carattere accentato nella sua relativa codifica (es. è diventa &egrave) e questo lo fai con le funzioni del php
    2. usi la regular expression in maniera che accetti non solo lettere ma anche caratteri alfanumerici quali #; ecc.

    L’esempio che ho fatto io, \D indicava che quel token accetta tutti i tipi di caratteri tranne i numeri, quindi una scelta valida se non hai bisogno di accettare numeri come in questo caso. Se vuoi accettare anche i numeri la togli del tutto.

    Non so se sono stato un pò più chiaro, nel caso chiedi e cerco di spiegarmi meglio! ;)

  18. Ade
    June 22, 2010 at 1:42 am

    riguardo all’inserimento dei caratteri accentati ho fatto questa espressione:
    prima codifico la stringa:
    $stringa=htmlentities($stringa,ENT_QUOTES,”UTF-8″);
    e dopo utilizzo questa espressione:
    preg_match(“/^([a-z\'\è\é\ì\à\ò\ù])+$/i”,$stringa)

    solo che mi funziona perfettamente per i caratteri accentati ecc.. ma non per l’apostrofo (')…
    dove sbaglio???
    grazie in anticipo!

  19. Ade
    June 22, 2010 at 1:47 am

    ops…:
    utilizzo questa espressione:
    preg_match(“/^([a-z\& #039;\& egrave;\& eacute;\& igrave;\& agrave;\& ograve;\& ugrave;])+$/i”,$stringa)

  20. Simone D'Amico ← http://simonedamico.com
    June 22, 2010 at 2:30 pm

    Ciao Ade,
    scusami ma nei commenti è sempre un macello inviare frammenti di codice.

    Se per te non è un problema inviami per mail la Regex corretta, così provo ad aiutarti nella maniera migliore.

  21. Ade
    June 22, 2010 at 10:57 pm

    inviata…spero ti sia arrivata! grazie

Leave a Reply