Traduzione a cura di Fabrizio Angius [qtsolutions -A-T- gmx.net]
English TOC.

10. Validazione dell'input

Validazione utilizzando espressioni regolari

Qualsiasi software per computer necessita di un input corretto per poter produrre dell'output corretto. Ecco perche' dobbiamo verificare tutto l'input proveniente da fonti non sicure (come un utente, un altro software, un file, ecc.). Questo processo viene definito 'validazione dell'input'.

C'e' anche un'altra ragione: sicurezza. Ormai molti sistemi supportano lo scripting, per esempio per prendere l'input utente da inserire in una interrogazione SQL su un database centralizzato. In queste situazioni e' fondamentale proteggere il sistema da iniezioni di codice o cose simili.

Come e' possibile validare l'input? Alcune cose sono banali, come la verifica di numeri che contengono i caratteri da 0 a 9 e magari +,-,., e oppure E se vogliamo utilizzare la notazione scientifica e i numeri reali. Altre cose come per esempio indirizzi e-mail, URL, ecc. sono piu' difficili da verificare, ma esiste pur sempre uno schema ('pattern' nel gergo) anche se magari complesso. Quando si ha a che fare con il linguaggio umano, questi pattern diventano talmente complessi e vari da non poter essere espressi da un programma per computer - basta provare gli algoritmi di correzione di un word processor per vedere quanto sono stupidi. In queste situazioni possiamo pur sempre fare un minimo di validazione per assicurarci che i dati non contengano caratteri non permessi.

Tutte queste situazioni possono essere affrontate utilizzando espressioni regolari o, in breve, regexp o, ancora piu' breve, RE. Quando state scrivendo una RE, state semplicemente definendo il pattern con il quale confrontare poi il testo di input.

Prima di andare oltre, ci sono un paio di cose da discutere. Prima di tutto, ci sono diversi tipi di RE; sono tutti molto simili ma differiscono per alcuni dettagli. In questo capitolo prenderemo in considerazione l'implementazione utilizzata da Qt. In secondo luogo, le RE possono essere utilizzate in molte situazioni, per esempio per suddividere l'input in piu' parti. Infine, in questo capitolo affronteremo solo le basi, raschiando un po' la superficie. Per ulteriori informazioni, date un'occhiata ai link per le letture consigliate presenti alla fine di questo capitolo.

La documentazione ufficiale di Qt sulle RE e' enorme e ben fatta. Cerchero' di fare una breve introduzione, lasciando il resto alla documentazione ufficiale.

La RE piu' semplice che si possa immaginare e' un singolo carattere, per esempio A. Questa RE corrisponde a una stringa contenente la lettera A. La A non si dve trovare in una posizione particolare, ci possono essere piu' A, ma ci deve essere almeno una A.

Mentre una RE di un solo carattere e' piuttosto limitante, possiamo permettere anche piu' caratteri. Per permettere cio', basta mettere un gruppo di caratteri tra parentesi quadre. La RE [ABC] corrisponde a una stringa contenente A, B o C. Come per la RE con un solo carattere, ci devono essere una o piu' occorrenze di uno di questi tre caratteri. Per evitare di dover scrivere tutto l'alfabeto per far individuare una lettera qualsiasi o per evitare di dover scrivere [0123456789] per i numeri, possiamo specificare delle sequenze scrivendo per esempio [A-Z] (tutte le lettere maiuscole dalla A alla Z), [a-z] (solo minuscole) e [0-9] (solo numeri).

Ci sono altre tre cose da capire prima di poter utilizzare delle RE per confrontare gran parte degli input.

Per prima cosa, affiancando singoli caratteri o gruppi di caratteri, vogliamo che questi appaiano esattamente in quell'ordine. Per esempio [ABC][123]a individua le stringhe A1a, xB3a, yvC1ax ma non x1Aay, ya1A n瞼span class="code">a. mail va bene con mailbox, junk mail, email, ecc.

In secondo luogo, possiamo indicare che un pattern si deve trovare all'inizio di una riga con ^ o alla fine di una riga con $. Questo significa che ^[ABC][123]$ va bene solo per A1, A2, A3, B1, B2, B3, C1, C2 e C3. Se utilizziamo uno di questi simboli, non permettiamo nessun carattere prima o dopo il nostro pattern.

Possiamo infine controllare il numero di ripetizioni permesse per un determinato carattere o gruppo di caratteri utilizzando {numero minimo, numero massimo}. In questo modo [A-Z]{0,15} individua da 0 a 15 lettere maiuscole e [0-9]{1,3} individua tutti i numeri tra 0 e 999.

Invece di specificare un minimo e un massimo, possiamo imporre un valore preciso scrivendo {occorrenze_esatte} oppure non specificando il valore minimo o massimo (in questo modo verra' ignorato da QRegExp). E' anche possibile scrivere, per esempio, [0-9]+. Questo individua una o piu' cifre. [0-9]* individua invece zero o piu' cifre, mentre [0-9]? individua zero o una cifra.

Il simbolo ^ all'inizio di un gruppo di caratteri funge da negazione. Per esempio [^0-9]$ ci assicura che l'ultimo carattere della stringa non sia un numero.

Ci sono due caratteri un po' problematici da individuare utilizzando i gruppi di caratteri. Il trattino, "-", deve essere posto come ultimo carattere in un gruppo, altrimenti viene interpretato come simbolo di sequenza (come a-z). Lo spazio, " ", va messo lo stesso alla fine, ma prima di un'eventuale trattino.

Proviamo a scrivere un pattern per individuare un numero di serie. Il numero e' cosi' definito: una singola lettera, W, S o G per indicare la serie del prodotto (workstation, server o gaming), un numero di quattro cifre, quindi una barra e un numero a due cifre seguito da un trattino e da un'altra cifra. W1003/01-26, S2055/99-2 e G9900/03-801 sono degli esempi validi. Quindi, per prima cosa individuiamo l'inizio della stringa con il simbolo ^, quindi la W, la S o la G utilizzando [WSG]. In numero a quattro cifre e' semplice: [0-9]{4}, lo stesso vale per la barra (ricordatevi che la barra rovescia, "\", e' un carattere speciale - date un'occhiata alla documentazione per ulteriori dettagli). Il numero a due cifre e il trattino sono lo stesso banali: [0-9]{2}-. L'ultima cifra la individuiamo con un [0-9]+. Indichiamo infine che la stringa deve terminare qui con un $. La RE completa e' ^[SWG][0-9]{4}/[0-9]{2}-[0-9]+$.

Il RegExpExperiment

Per rendere piu' semplice la scrittura e la verifica delle RE, questo capitolo contiene una banalissima applicazione chiamata RegExp Experiment. Progettazione ed implementazione sono discussi qui di seguito.

L'applicazione sara' formata da un dialog con due line edit, uno per il testo da controllare e uno per la RE. Il risultato verra' mostrato con una label e ci saranno due pulsanti: check e close. Allora iniziamo.

Per prima cosa, create una nuova cartella e dentro di essa create un nuovo progetto con il Designer di Qt.

Aggiungete un dialog al nuovo progetto e inseriteci 2 pulsanti, 2 line edit, 4 label e 2 spacer come mostrato nella figura 10-1. La figura mostra anche i layout. I 2 per 3 widget superiori finiscono in un grid layout. I pulsanti e lo spacer orizzontale in un layout orizzontale. Il resto finisce infine in un layout verticale.

I widget e il layout

Figura 10-1 I widget e il layout.

Ora cambiate le stringhe in base alla figura 10-2. Chiamate il line edit superiore leText e quell'altro leRE. Il pulsante di sinistra si chiama bCheck mentre l'altro si chiama bClose. La label sotto ai line edit si chiama lResult. Chiamate anche il dialog fRegExpExperiment e impostate la caption su "RegExp Experiment". Salvate il tutto come fregexpexperiment.ui

Le stringhe

Figura 10-2 Le stringhe.

Ora fatte un doppio click sul form per creare fregexpexperiment.ui.h. Aggiungete quindi due slot protetti: init() e check() (utilizzando l'object explorer). Sistemate le connessioni tra segnali e slot come mostrato dalla figura 10-3.

Le connessioni

Figura 10-3 Le connessioni.

L'esempio 10-1 mostra l'implementazione dei due slot. init() inizializza lResult affinche' non mostri nessun testo strano, check() utilizza un'istanza di QRegExp per confrontare il testo utilizzando l'espressione regolare indicata. Assicuratevi di includere <qregexp.h> nell'implementazione del dialog (utilizzando l'object explorer).

void fRegExpExperiment::init()
{
  lResult->setText( "n/a" );
}

void fRegExpExperiment::check()
{
  QRegExp re( leRE->text() );

  lResult->setText( QString::number( re.search( leText->text() ) ) );
}

Esempio 10-1

Il metodo QRegExp::search( ... ) ritorna la prima posizione in cui il pattern e' confrontato con successo o -1 se l'espressione regolare non puo' essere individuata nel testo. Se si sta confrontando l'inizio di una stringa il valore ritornato dovra' quindi essere 0 oppure -1.

Per completare il progetto, aggiungete un file C++ con una banale funzione main() come mostrato dall'esempio 10-2.

#include <qapplication.h>

#include "fregexpexperiment.h"

int main( int argc, char **argv )
{
  QApplication a( argc, argv );

  fRegExpExperiment *m = new fRegExpExperiment();
  a.setMainWidget( m );
  m->show();

  return a.exec();
}

Esempio 10-2

Eseguite infine qmake && make dalla console e dovrebbe funzionare. Ora fatte qualche esperimento con le RE descritte nella prima sezione di questo capitolo.

Validare l'input utente utilizzando QValidator

Ora sappiamo come individuare e scrivere una RE. Ora dobbiamo applicare il tutto al nostro grandioso progetto Qt senza troppi sforzi. Il tutto viene fatto utilizzando sottoclassi di QValidator come QRegExpValidator e QDoubleValidator.

Un validatore puo' essere assegnato a line edit, combo box, spin box, ecc. Una volta assegnato, valida l'input e ci dice se e' valido, se e' da correggere o se non e' per niente valido. Se non e' valido l'utente lo deve correggere prima di poter chiudere il widget. Se l'input e' da correggere se ne puo' occupare l'utente o ci potrebbe provare il validatore prima di chiudere il widget. Nel caso di dati validi non e' necessara nessuna azione specifica. L'esempio 10-3 mostra come assegnare il nostro numero di serie a un line edit utilizzando QRegExpValidator.

QRegExp re( "^[SWG][0-9]{4}/[0-9]{2}-[0-9]+$" );
QRegExpValidator *validator = new QRegExpValidator( re, 0 );

QLineEdit *leSN = new QLineEdit( parent );
leSN->setValidator( validator );

Esempio 10-3

Le classi QIntValidator e QDoubleValidator possono essere utilizzate per verificare che l'input sia un numero valido compreso in un certo limite. Se non riuscite a validare l'input con una delle sottoclassi di QValidator e' abbastanza sottoclassare quest'ultima. I dettagli sono descritti nella documentazione ufficiale per QValidator.

C'e' un'ultimo dettaglio. Quando utilizzate setText per cambiare il testo di, per esempio, un line edit, il validator non viene utilizzato. Dovete assicurarvi personalmente della correttezza del valore.

Riassunto

Il codice sorgente per questo capitolo puo' essere scaricato qui.

Letture consigliate

This is a part of digitalfanatics.org and is valid XHTML.