PHP/Programmazione/Regexp

Wikibooks, manuali e libri di testo liberi.
Indice del libro

Le espressioni regolari consentono complesse elaborazioni testuali.

In questa sede ci limiteremo a illustrare il funzionamento delle regex su PHP senza però spiegare effettivamente come si scrivono le regular expressions (cosa infatti non semplice e di certo impossibile da fare in una pagina).

Per le espressioni regolari sono state scritte varie funzioni, quelle più famose sono la coppia _ereg e il package PCRE (acronimo di "Perl Compatible Regular Expressions" ovvero Regex compatibili con Perl). Le prime tuttavia non hanno le stesse funzionalità avanzate del package PCRE, pertanto analizzeremo queste ultime in particolare (se però si vuole avere una visione anche sulle prima, basta visitare questo link).

PCRE[modifica]

Questa libreria è un porting in PHP di un'altra molto utilizzata e diffusa, scritta per Perl. Essa viene anche chiamata colloquialmente preg_match.

Ottenere dei dati[modifica]

Vediamo un esempio di come si utilizza una regex in PHP:

<?php
$regex = "/\[\[[Ii]m(?:age|magine):(.*?)\]\]/";
$testo = "[[Immagine:Ciao.jpg]], [[image:hello.jpg]], image:hello.jpg";
preg_match($regex, $testo, $risultato);
print_r($risultato);
?>

Se proviamo a eseguire questo script, otterremo come risultato:

Array
(
    [0] => [[Immagine:Ciao.jpg]]
    [1] => Ciao.jpg
)

La regex utilizzata serve per trovare tutte le immagini in markup wiki; in particolare restituisce il suo nome reale (ovvero, senza il namespace immagine).

In PHP una regex deve essere definita in una stringa di testo che inizia e finisce con "/" per far capire al motore che quella è una regex e non testo qualunque. La funzione preg_match prende il primo parametro (la regex), lo confronta col secondo (il testo) e assegna il risultato alla variabile indicata come terzo parametro (nel nostro caso $risultato , che noi mostriamo con la funzione print_r).

La variabile di risultato conterrà il testo associato dall'intera regex e l'evenutale testo dei cosiddetti backreference indicati tra le parentesi (nel nostro caso rispettivamente $risultato[0] e $risultato[1]). La prima parentesi non viene restituita come backreference solo perché è stato utilizzato il modificatore ?: che indica al motore delle regex di non contare quelle determinate parentesi per le backreference (ciò serve per rendere la regex più veloce).

Perché, però, non prende anche image:hello? Perché preg_match restituisce solo la prima occorrenza, per ottenerle tutte bisogna usare preg_match_all:

<?php
$regex = "/\[\[[Ii]m(?:age|magine):(.*?)\]\]/";
$testo = "[[Immagine:Ciao.jpg]], [[image:hello.jpg]], image:hello.jpg";
preg_match_all($regex, $testo, $risultato);
print_r($risultato);
?>

Risultato:

Array
(
    [0] => Array
        (
            [0] => [[Immagine:Ciao.jpg]]
            [1] => [[image:hello.jpg]]
        )

    [1] => Array
        (
            [0] => Ciao.jpg
            [1] => hello.jpg
        )

)

In questo modo abbiamo quindi sia "il pezzo di codice" che volevamo passare al parser sia il risultato nello specifico.

Abbiamo quindi due array annidati dentro un altro, pertanto per accedere al primo basterà usare $risultato[0], mentre $risultato[1] per il secondo. Per accedere invece, per esempio, a Ciao.jpg, basterebbe utilizzare $risultato[1][0].

Sostituire dei dati con degli altri (replace)[modifica]

Poniamo per esempio di dover cambiare tutte le stringhe di wikicodice [[Image:x]] con [[Immagine:x]] per ovviare a degli ipotetici problemi tecnici del motore wiki.

<?php
$testo = "[[image:ciao.png]], [[Image:CIAO.jpeg]]";
$regex = "/\[\[[Ii]mage:(.*?)\]\]/";
$regex2 = "[[Immagine:\\1]]";
$risultato = preg_replace($regex, $regex2, $testo);
print $risultato;
?>

Il risultato di questo script è:

[[Immagine:ciao.png]], [[Immagine:CIAO.jpeg]]

In questo modo abbiamo definito una prima regex per ottenere le sottostringhe e poi una seconda per indicare con cosa sostituire il testo associato dalla prima. Dato che la seconda è una stringa e la backslash (\) è un carattere speciale in PHP, bisogna farne l'escape(aggiungendo quindi un'altra backslash) per ottenere un backreference (riferimento all'indietro, ovvero andare a prendere la parentesi identificata dal numero).

Attenzione:

  1. preg_replace restituisce un valore tramite assegnamento (ovvero, dovete usare la sintassi $variabile = preg_replace();) al contrario di quanto faceva preg_match[_all])
  2. Questa regex non è la migliore in quanto il tutto potrebbe essere fatto semplicemente sostituendo "/\[\[[Ii]mage:/" con "[[Immagine". La parte superflua è stata tuttavia inserita a esclusiva finalità didattica (per spiegare come usare le backreference correttamente).

Modificatori[modifica]

Modificatore Descrizione
i Rende l'espressione case-insensitive.
m Permette la modalità multi-riga, così ^ si ancora all'inizio di ogni frase e $ alla fine sempre di ogni frase.
s Permette la modalità mono-riga, così ^ si ancora all'inizio del testo e $ alla fine (di tutto il testo). Il metacarattere . può essere utilizzato per catturare anche gli a capo.
x Modalità estesa, gli spazi dentro la regex fuori da espressioni vengono ignorati, permette inoltre di commentare le regex con #.
e La stringa (la $regex2 usata sopra) usata in preg_replace viene elaborata come codice PHP.
A Àncora la regex per forza all'inizio del testo.
D Il carattere $ àncora solo a fine testo.
u Modalità Unicode.

I modificatori servono a modificare il comportamento del motore di regex utilizzato. Per esempio, la regex usata sopra per italianizzare tutte le immagini, diventerebbe:

<?php
$testo = "[[image:ciao.png]], [[Image:CIAO.jpeg]]";
$regex = "/\[\[image:(.*?)\]\]/i";
$regex2 = "[[Immagine:\\1]]";
$risultato = preg_replace($regex, $regex2, $testo);
print $risultato;
?>

Con risultato: [[Immagine:ciao.png]], [[Immagine:CIAO.jpeg]]

Come si può vedere, il risultato è identico sebbene l'espressione regolare non sia la stessa (notare in particolare la i dopo l'ultimo slash - / - della regex).

Particolarità[modifica]

Le backreference (quelle di solito identificate come \1 o \\1 in php) possono anche essere richiamate con $1 (sostituendo l'1 al numero desiderato) come si fa in Perl.

Bibliografia[modifica]

Collegamenti esterni[modifica]