Stringhe ed espressioni regolari
Termini che devi imparare
- L'operatore == e ===
- Espressioni regolari
- PCRE
In questa sezione imparerai a verificare se due stringhe sono uguali oppure se una stringa
esiste all'interno di un'altra.
Comparazione con == e ===
La via più comune per paragonare due variabili in PHP è usare l'operatore
==. Quando l'opratore testa l'equivalenza di due entità, prima riduce le entità ad
un tipo comune. Questo causa conseguenze indesiderate. Per esempio il seguente codice dice
che $a e $b sono uguali:
$a = 'Hello World';
$b = 0;
if($a == $b) {
print "\$a e \$b sono uguali\n";
}
else {
print "\$a e \$b sono diversi\n";
}
La ragione di tutto ciò è che $a è un tipo stringa e $b è un tipo intero,
cosicchè il motore Zend di PHP ha necessariamente bisogno di convertirla in un intero.
== è un operatore debole, infatti converte i tipi nei tipi più "facili" comunemente
interi. La rappresentazione intera di 'Hello World' è 0 e così 0 = 0! == dovrebbe
essere usato solo per comparare stringhe.
PHP dispone di un operatore di comparazione più forte (===). Laddove == era troppo debole,
=== adesso è troppo forte. Infatti questo operando necessita che i due tipi siano del tutto
dello stesso tipo affinchè la comparazione possa avere un effetto aspettato. Il seguente
codice dirà che $a e $b non sono uguali:
$a = 1;
$b = "1";
if( $a === $b ) {
print "\$a e \$b sono uguali\n";
}
else {
print "\$a e \$b sono diversi\n";
}
Questo succede perchè internamente $a e $b sono tipi diversi quindi differenti sebbene
abbiano tutti e due lo stesso contenuto. Per questo motivo === potrebbe essere pericoloso quando
non si sa per certezza se i tipi sono diversi
Consiglio
Puoi forzare una variabile a stringa facendo un cast di questo tipo:
if( (string)$a == (string)$b) {...}
Questo convertirà sia $a che $b in stringhe prima della conversione dell'operatore
==. Questo produrrà il risultato aspettato sebbene sia un po' strano. Usare strcmp generalmente
è preferibile
strcmp & co.
Il modo migliore per comparare due variabili stringhe è quello di usare
la funzione strcmp(). strcmp() prende due argomenti e li compara
lessigraficamente (cioè in ordine di dizionario).
strcmp() ritorna 0 se le due stringhe sono identiche. Il codice che prima ci dava dei problemi
adesso darà l'output corretto:
$a = 1;
$b = "1";
if( strcmp($a,$b) == 0 ) {
print "\$a e \$b sono uguali\n";
}
else {
print "\$a e \$b sono diversi\n";
}
Se i due operandi non sono uguali strcmp ritornerà -1 se il primo operando
appare prima del secondo operando nel vocabolario e 1 se il secondo operando appare
prima del primo in un vocabolario.
strcasecmp() è identica a strcmp() fatta eccezione che strcasecmp() fa comparazioni
case insensitive.
$a = 'hello';
if( strcasecmp( $a, 'HELLO' ) == 0) {
print "\$a è uguale a HELLO\n";
}
Confrontare porzioni di stringhe
Fino ad adesso abbiamo visto come "matchare" (dall'inglese to match) stringhe esatte ma a volte
(anzi molto spesso) avrai bisogno di trovare solo una parte di stringa. Quando ci riferiamo ad
porzione di stringa la chiameremo sotto stringa (sub string in inglese). Una sub-string è
una qualsiasi sotto stringa della stringa.
sub-string matching ad offset iniziale
Per matchare/trovare solamente la sotto stringa iniziale, PHP disponde di una famiglia di funzioni:
strncmp(). strncmp() e strncasecmp() sono identiche a strcmp() e a strcasecmp() tranne per il
fatto che entrambe hanno un terzo parametro, n, che ordina PHP di controllare solamente i primi n
caratteri. Quindi
strncmp( 'figura1.gif', 'figura2.gif',6 )
tornerà
0 perchè le prime sei lettere degli argomenti sono uguali.
sub-string matching ad offset generico
Se, invece, devi determinare che una stringa sia o no contenuta in un'altra stringa data,
possiamo usare la funzione strstr(). strstr() prende come primo argomento
la stringa in cui cercare (chiamata
soggetto), e come secondo
argomento la stringa da cercare (chiamato
pattern). Se strstr() trova
la stringa , ritornerà la stringa cercata più tutta la stringa restante. Altrimenti
ritornerà false.
Il seguente codice serve per determinare se la stringa $strign contiene o no la stringa 'PHP':
if( strstr( $string, 'PHP' ) !== FALSE ) {
// fai qualcosa
}
Se vuoi cercare una sotto stringa non considerando il case puoi usare la funzione stristr()
if( stristr( $string, 'PhP' ) !== FALSE ) {
// fai qualcosa
}
Se al posto della stringa vuoi avere la posizione del pattern all'interno della stringa
allora potrai usare la funzione strpos(). La funzione strpost() lavora similarmente alla
funzione strstr() tranne per due motivi:
-
anzichè ritornare la sotto stringa contenente il match, strpos() ritorna
la posizione (l'offset) dell'inizio del match
-
strpos() accetta un terzo parametro che permette di controllare a partire da un offset
indicato
Di seguito un esempio di strpos() per trovare tutti gli offset della stringa PHP nella stringa soggetto
$offset = 0;
$match_pos = array();
while( ($offset = strpos( $string, 'PHP', $offset )) !== FALSE) {
$match_pos = $offset++;
}
strpos() ha anche una sorella stripos() per ottenere la posizione ignorando il case.
Consiglio
Siccome il primo carattere di una stringa si trova all'offset 0, devi sempre usare ===
per controllare il match con strpos(). Se non sai il perchè leggi meglio questo capitolo.
Estrazione delle informazioni dalle stringhe
Quando le informazioni arrivano dall'esterno (come per esempio con i form), informazioni
molto complesse sono contenute nelle nostre stringhe ed è necessario estrarle. Esempi
possono essere la decomposizione dei numeri di telefono, delle emails, carte di credito ecc.
Php ci offre ottime funzioni per l'estrazione di dati con formati fissati ma anche tutta la
potenza delle espressioni regolari (che vedremo a breve).
sub string by offset
Per estrarre una sotto stringa dato l'offset, puoi usare la funzione substr(). substr()
prende come primo argomento una stringa ( il
soggetto), come
secondo prende un offset che determina l'inizio della ricerca per la funzione e opzionalmente
un terzo argomento che specifica la lunghezza della sotto stringa da estrarre.
Per esempio, se volessi ottenere tutta una stringa tranne il primo carattere:
$result = substr($string, 1);
o per ottenere i primi otto caratteri di una stringa
$result = substr($string, 0, 8);
Considera, adesso, il seguente codice che estrae la parte locale di una email (quella prima della
@ per intenderci):
$emailLocale = substr( $email, 0 , strpos( $email, '@' ));
Se vuoi ottenere una sotto stringa partendo dalla fine, basta specificare un offset negativo:
$result = substr($email, -4);
Questo memorizzerà dentro $result gli ultimi 4 caratteri dell'email passata come
soggetto.
Se hai necessità di accedere ai caratteri singoli di una stringa, puoi usare { e }
per accedere agli offset desiderati come mostrato in questo esempio:
$len = strlen( $string );
for( $i = 0; $i < $len; $i++ ){
if($i % 2) {
$string{$i} = strtoupper( $string{$i} );
}
}
Estrazione formattata
Se le informazioni sono troppo complesse per essere estratte semplicemente allora si possono usare
le espressioni regolari. Ma se una certa tipologia di informazioni può essere rappresentata
con l'uso di printf() allora puoi usare sscanf() per estrarre informazioni.
Per esempio, per estrapolare i dati da un ip con porta (Es.: 127.0.0.1:6137) puoi usare il formato
"%d.%d.%d.%d:%d". Si può usare con sscanf() come segue:
$parti = sscanf( $string, "%d.%d.%d.%d:%d");
Se $string è uguale a 127.0.0.1:6137 allora $parti sarà costruita così:
Array {
[0] => 127
[1] => 0
[2] => 0
[3] => 1
[4] => 6137
}
Il pattern di sscanf() deve essere assolutamente identico per funzionare. In effetti questo è
un modo un po' fragile di eseguire una estrapolazione dei dati.
Modificare le stringhe
In questa sezione, imparerai a modificare le stringhe sostituendole con altre sia specificando
un offset dal quale fare la sostituzione sia sostituendo semplicemente all'interno della stringa.
Modificare sotto stringhe dato l'offset
Per rimpiazzare una sotto stringa in una stringa soggetto, useremo la funzione substr_replace().
Il suo primo argomento è il soggetto, il secondo è la stringa rimpiazzo, il terzo
argomento è l'offset di partenza e, opzionale, quarto argomento lunghezza per il rimpiazzo.
Per meglio illustrare consideriamo il seguente esempio che sostituisce tutte le cifre di una carta
di credito, tranne le ultime 4, con delle X.
$len = strlen( $ccNum );
$newnum = substr_replace( $ccNum, str_repeat( 'X', $len-4), 0, $len-4);
Primo, troviamo la lunghezza della carta di credito in questione, e poi sostituiamo le prime
$len - 4 caratteri con un numero uguale di X.
Sostituire sotto stringhe
Un altro task comune è quello di sostituire semplicemente tutte le occorrenze di una stringa
all'interno di un soggetto. La funzione migliore per fare ciò è str_replace().
str_replace() prende come primo argomente il pattern da ricercare e sostituire,
come secondo ciò che va sostituito e come terzo la stringa soggetto in cui deve essere
effettuato lo scambio. Prendiamo come esempio una funzione che sostituisce :) con un link ad una
immagine (come viene fatto nel mio guestbook ;) )
$new_subject = str_replace( ':)' , '<img src = "/smiley.jpg" />', $soggetto);
Spesso avrai bisogno di fare sostituzioni case insensitive: potrai usare str_ireplace().
Sia str_replace() che str_ireplace accettano anche degli array per tutti i loro parametri.
Se i primi due sono array, allora tutte le sostituzioni verranno fatte in una sola chiamata. Se
il terzo argomento è un array allora le sostituzioni verranno effettuate su tutti i valori
dell'array.
$emoticons = array( ':)' => '<img src = "/smiley.jpg" />',
';)' => '<img src = "/wink.jpg" />',
':(' => '<img src = "/sad.jpg" />');
$new_subject = str_replace( array_keys($emoticons), array_values($emoticons), $soggetto );
Espressioni regolari
Le funzioni più potenti per la manipolazione di stringhe sono le espressioni
regolari. Le espressioni regolari hanno un robusto linguaggio per specificare pattern per l'estrazione
o la sostituzione di parti di testo.
Esistono due tipi di espressioni regolari: PCRE e POSIX. PCRE significa Perl Compatible
Regular Expression e vengono chiamate così perchè sono compatibili con gli script
fatti in perl.Le espressioni regolari POSIX supportano, invece, lo standard POSIX sulle espressioni
regolari. Le funzioni POSIX per le espressioni regolari (famiglia
ereg_ e split()) sono più lenti rispetto alle PCRE
e di solito il loro uso non è incoraggiato.
PCRE: sintassi di base
Un pattern per una espressione regolare consiste in una stringa di testo con meta caratteri.
Questi metacaratteri definiscono il tipo e il numero di caratteri che possono combaciare con
un particolare pattern.
Il set base di metacaratteri è la classe di caratteri riassunta nella tabella seguente
| Metacaratteri | Corrispettivo (Match) |
| \d | numeri\digit [0-9] |
| \D | Tutto tranne numeri\digit |
| \w | Qualsiasi carattere alfanumerico o underscore (_) |
| \W | Qualsiasi carattere che non sia alfanumerico o underscore |
| \s | Qualsiasi whitespace (spazio, tab, newline ecc.) |
| \S | Qualsiasi NON whitespace |
| . | Qualsiasi carattere tranne il newline |
Questo set di metacaratteri fa il match con solo un carattere. Affinchè
questi pattern siano utili dovremo avere la possibilità di enumerarli. Le PCRE
supportano questa possibilità. Di seguito un riassunto:
| Metacaratteri | Corrispettivo (Match) |
| ? | Occorre 0 o 1 volta |
| * | Occorre 0 o più volte |
| + | Occorre 1 o più volte |
| {n} | Occorre n volte |
| {,n} | Occorre al più n volte |
| {m,} | Occorre al meno m volte |
| {m,n} | Occorre tra m ed n volte |
Mettendo questi elementi tutti insieme si possono formare numerosi pattern come per esempio
un CAP americano formato da XXXXX-XXXX
if(preg_match( "/\d{5}-\d{4}/", $soggetto )) {
//Trovato un CAP americano
}
Il primo argomento è il pattern di ricerca mentre il secondo è il soggetto su cui
effettuare la ricerca. Da notare che il pattern dell'espressione regolare è incluso
tra due slash (/).
A differenza di sscanf(), il match di preg_ vale in tutto il soggetto ovunque il pattern
si trovi. Se vuoi che il pattern valga solo all'inizio allora dovrai utilizzare ^.
Potrai trovare la fine con $. Quindi, se vuoi verificare che il soggetto sia solo ed esclusivamente
un Codice di avviamento postale americano il codice di prima diventa:
if(preg_match( "^/\d{5}-\d{4}$/", $soggetto )) {
//Trovato un CAP americano
}
Se vuoi che la ricerca dia esito positivo, però, solamente perchè il CAP inizi con
alcuni numeri allora potrai aggiungere un selezionatore come
[2-9]
per dire che i cap validi sono quelli che iniziano con un numero che va da 2 a 9.
Estrarre informazioni con le espressioni regolari
Di solito dovrai fare di più che verificare l'esistenza di un determinato pattern in una
stringa. A volte dovrai estrarre il MATCH dalla stringa per poterlo elaborare, memorizzare o
verificare. Se vuoi catturare dei pattern dovrai semplicemente includerli in delle parentesi
tonde:
/(\d{5})-(\d{4})/
Dopo aver specificato ciò che si vuole catturare basterà passare alla funzione
preg un array al fine di memorizzare i pattern. L'ordine viene considerato contando i pattern
da sinistra verso destra. Esempio:
$string = "Il mio ZIP code è 21797-2046";
if(preg_match( "/(\d{5})-(\d{4})/", $string, $array )) {
print_r( $array );
}
L'output sarà
Array {
[0] => 21797-2046
[1] => 21797
[2] => 2046
}
Consiglio
preg_match controlla solamente la prima occorrenza del pattern nella stringa.
Se vuoi che il match venga eseguito più volte nella stringa dovrai eseguire preg_match_all
Sostituire pattern con espressioni regolari
Le espressioni regolari ti permettono di sostituire il pattern con qualsiasi cosa tu voglia.
Fare sostituzioni con le espressioni regolari è come con str_replace() tranne per il fatto
che con le espressioni regolari la sostituzione non avviene con una string prefissata ma con
il pattern stesso.
Per effettuare una sostituzione di un pattern devi usare preg_replace(). Il suo primo argomento
è una espressione regolare che dovrebbe individuare il pattern all'interno della stringa.
Il secondo argomento è ciò che deve andare al posto del pattern che può essere
una stringa letterale o un argomento del pattern stesso individuato da \n (con n numero del pattern
individuato). Il terzo argomento è ovviamente il soggetto.
$soggetonuovo = preg_replace("/(\S+)@(\S+).(\S+)/", '\1 at \2 dot \3', $soggetto);
Questo codice convertirà una stringa tipo demenziale@altervista.org
in demenziale at altervista dot org.
Divisione di stringa in componenti
PHP offre 3 funzioni per la divisione di stringhe in parti: explode(), split() e preg_split().
explode() è la più semplice delle tre. Permette di dividere una stringa
con un delimitatore statico. Esempio:
$info = array();
$file = file( "/etc/passwd" );
foreach( $file as $linea ) {
$info[] = explode( ":", $linea );
}
Non avendo espressioni regolari al suo interno, explode() è la più veloce delle tre.
Se possibile dovresti preferirla rispetto a preg_split() o a split().
split() è una espressione regolare di POSIX, più lenta di preg_split().
preg_split() attraverso le espressioni regolari è molto potente e, per esempio, permette di
dividere in base ai whitespaces, una qualsiasi stringa in questo semplice modo:
$parti = preg_replace( "/\s+/", $soggetto );
P H P
D E V E L O P M E N T