Cambiamo ora completamente argomento, e passiamo ad analizzare alcuni temi un poco più specialistici, che però ritornano spessissimo nella pratica della programmazione.
Per esempio, nell'uso che abbiamo fatto delle funzioni di trattamento di
stringhe del C, è possibile che qualcuno abbia notato una certa maggiore
complicazione, rispetto per esempio al Fortran (dove però
sono completamente assenti i concetti
di puntatori, di allocazione dinamica della memoria, e dunque di stringhe
che cambiano dimensione en route).
Si può essere d'accordo con questa affermazione, ma non
bisogna dimenticare che in compenso la libreria
standard C permette di accedere ad alcune librerie di
trattamento delle stringhe che affrontano il problema
in modo decisamente più moderno.
Uno dei problemi che spesso si presentano nella scrittura dei programmi
è quello del confronto di stringhe. Può essere ad esempio necessario
controllare il contenuto degli argomenti ricevuti dall'utente, oppure
letti da un file. E' buono stile di programmazione che questi controlli
siano fatti nel modo più robusto (il programma non deve impazzire
se riceve qualcosa di strano) e tollerante (il programma deve sapere
accettare anche forme diverse, ma simili dello stesso argomento) possibile.
Elaborare stringhe in C con questi criteri può essere assai tedioso,
oltre che incline ad errori. Esiste tuttavia un potente algoritmo simbolico, il
cui uso sta diventando sempre più standard, che permette di
rappresentare
intere famiglie di stringhe, semplificando dunque il confronto. Si tratta
delle cosiddette regular expressions, che passiamo ora a descrivere
con la dovuta attenzione.
Alcune considerazioni generali:
> dir *.exe
"La Vispa Teresa" =~ /Vispa/
"La Vispa Teresa" =~ /Vi..a T....a/
"La Vispa Teresa?" =~ /Teresa\?/
"La Vispa Teresa" =~ /La.*Teresa/
"avea tra l'erbetta" =~ /erbet+a/
"La 'Vispa Teresa'" =~ /La '?Vispa/ =~ "La Vispa Teresa"
/^La Vispa/ =~ "La Vispa Teresa" !~ /^Vispa/
/Teresa$/ =~ "La Vispa Teresa" !~ /Vispa$/
/V[ispa]{4,4} T[ersa]{5,5}/ =~
"La Vispa Teresa" =~
/La V[A-Za-z]{4,4} T[A-Za-z]{5,5}/
/V[^jkwxy]+ T[^jkwxy]+/ =~
"La Vispa Teresa" =~
/[^ ]+ Teresa/
=~ "La Furba Teresa"
"La Vispa Teresa" =~
/(Vispa|Furba) Teresa/ =~
"La Furba Teresa"

Ecco un breve estratto della guida pratica di Perl, con indicate le parti che sono specifiche di Perl:


La libreria standard POSIX di R.E. è accessibile attraverso tre chiamate:
#include <regex.h>
int regcomp(regex_t *preg, const char *regex, int cflags);
viene utilizzata per compilare la R.E. in un formato che faciliti
poi le operazioni di confronto. La struttura che contiene le informazioni
ricavate dalla compilazione, di tipo regex_t, deve essere
passata alla funzione. cflags è la combinazione in OR di alcuni
parametri che determinano la funzionalità del confronto. Di solito si usa
sempre REG_EXTENDED, che seleziona le funzioni estese
(vedi sopra), e si può aggiungere REG_ICASE se il confronto
non deve distinguere fra maiuscole e minuscole, e/o REG_NOSUB,
se si desidera che non vengano identificate le posizioni della stringa
dove si verifica l'occorrenza della R.E.
int regexec(const regex_t *preg, const char *string,
size_t nmatch, regmatch_t pmatch[], int eflags); esegue fisicamente il
confronto della stringa string con la R.E. pre-compilata in
preg. eflags non è tipicamente necessario,
e può essere lasciato a zero. Se non è stato utilizzato
in compilazione il flag
REG_NOSUB, è possibile passare alla funzione un array di strutture
di tipo regmatch_t di dimensione nmatch
che conterrà le posizioni delle occorrenze trovate. Questa la definizione
di regmatch_t, la struttura che contiene la posizione
iniziale e finale (+1) delle occorrenze trovate sia per l'intera R.E.
(pmatch[0]) che per
le parti di R.E. delimitate da parentesi tonde (negli elementi successivi di
pmatch):
typedef struct
{
regoff_t rm_so;
regoff_t rm_eo;
} regmatch_t;
La funzione restituisce zero se il match ha successo,
altrimenti REG_NOMATCH.
void regfree(regex_t *preg); La struttura preg
è stata collegata durante la compilazione a strutture dati allocate
dinamicamente: non bisogna dimenticarsi di rilasciarle con questa
chiamata quando non sono più necessarie.
Questo
prova a convertire in un numero da 1 a 12 il nome di
un mese, scritto in italiano, inglese, francese o tedesco.
Esercizio: Provare a scrivere un programma che interpreta una data completa utilizzando una R.E.