02. fscanf()

La funzione fscanf() consente di leggere dei dati da un file precedentemente aperto con la funzione fopen().

Il funzionamento della funzione fscanf() è del tutto simile a quella della scanf() fatto salvo che prevede un parametro in più tramite il quale è indicato il descrittore del file.

In questo esempio vengono letti e stampati a video i dati presenti in un file.

Il file è organizzato in modo tale che su ogni riga sia presente una coppia di valori numerici (matricola e voto).

Prima di eseguire il programma è necessario creare nella cartella corrente del programma, che nel caso si usi Code::Block coincide con la cartella principale del progetto, il file voti.txt con i valori riportati in calce.


#include <stdio.h>

int main()

{

int matr, voto, ret;

FILE *fd;

fd=fopen("voti.txt", "r");

if (fd==NULL){

printf("Errore nell' apertura del file\n");

return 1;

}

do{

ret = fscanf(fd, "%d %d", &matr, &voto);

if (ret!= EOF)

printf("matr = %d voto %d\n", matr, voto);

}while (ret!= EOF);

fclose(fd);

return 0;

}


La variabile fd, che richiama i termini file descriptor, è inizializzata tramite la funzione fopen() che apre il file "voti.txt" (presente nella cartella corrente del programma) in modalità di lettura ("r"). Questo significa che sul file si possono solo effettuare operazione di lettura ma non di scrittura.

Se le funzione fopen() restituisce la costate NULL (definita in stdio.h), vuol dire che l'apertura del file è fallita.

EOF è una ulteriore costante definita in stdio.h (il nome richiama i termini End Of File) ed è utilizzata per verificare se la lettura del file è giunta alla fine. Infatti la funzione fscanf() restituisce il numero di caratteri letti ma, nel caso si sia giunti alla fine del file, restituisce EOF.


File voti.txt

6785 7

2222 5

3333 8


L'uso della fscanf() si presta molto bene a leggere file organizzati in campi separati da uno specifico carattere, ad esempio la virgola. Questo consente di alternare dati numerici e stringhe alfabetiche.

Nell'esempio che segue ci poniamo in una situazione di tipo generale con la presenza di un campo costituito da "nome e cognome" che, come sappiamo, potrebbe contenere due o più parole.

Il separatore utilizzato è la virgola e va tenuto presente che alla fine di ogni riga vi è un carattere "invisibile" che corrisponde al '\n'.


#include <stdio.h>

int main()

{

int matr, voto, ret;

char nome[100];

char c;

FILE *fd;


fd=fopen("voti.txt", "r");

if (fd==NULL){

printf("Errore nell' apertura del file\n");

return 1;

}


do{

ret = fscanf(fd, "%[^,]%c%d%c%d%c", nome, &c, &matr, &c, &voto, &c);


if (ret!= EOF)

printf("nome: %s matr: %d voto: %d\n", nome, matr, voto);

}while (ret!= EOF);


fclose(fd);


return 0;

}


Il programma richiede la presenza del file voti.txt strutturato come nel seguente esempio.

File voti.txt

Mario Rossi,6785,7

Maria Giovanna D'Elmi,7772,8

Antonio Di Rosa,6787,7


Un commento particolare merita la fscanf() dove compare una nuova regola di lettura fin qui ancora non analizzata.

La sequenza speciale "%[^,]" ha il seguente significato: richiede la lettura di un testo il cui delimitatore è costituito dal carattere riportato nelle parentesi quadre dopo il carattere ^. In questo caso la virgola.

La stringa di formattazione della scanf() è così strutturata:

  • "%[^,]": acquisisce una successione di caratteri fino ad incontrare il separatore (in questo caso la virgola). I caratteri letti sono memorizzati nell'array nome sotto forma di stringa (quindi viene aggiunto il carattere '\0'). Il separatore non è estratto.

  • "%c": viene estratto un carattere, corrispondente al separatore. Esso viene memorizzato nella variabile c.

  • "%d": viene estratto un numero di tipo intero ed è memorizzato nella variabile matr.

  • "%c": viene estratto un carattere, corrispondente al separatore. Esso viene memorizzato nella variabile c.

  • "%d": viene estratto un numero di tipo intero ed è memorizzato nella variabile voto.

  • "%c": viene estratto un carattere, corrispondente al '\n'. Esso viene memorizzato nella variabile c.

Note:

La funzione di lettura così come è stata impostata ha un punto di vulnerabilità derivante dal fatto che non è controllata la lunghezza del primo campo, corrispondente a "nome e cognome".

L'array predisposto per la lettura è piuttosto grande (100 caratteri) ma resta il fatto che non c'è nessun controllo sul numero di caratteri letti.

Un'altra annotazione riguarda la modalità utilizzata per "consumare" i caratteri impiegati come separatore e il carattere di fine riga. In effetti il carattere da "consumare" è acquisito nella variabile c che però non viene usata.

La formattazione utilizzata di seguito, risolve entrambi i problemi.

ret = fscanf(fd, "%99[^,]%*c%d%*c%d%*c", nome, &matr, &voto);

Il numero 99 rappresenta il massimo numero di caratteri da leggere nell'array nome (è usato 99 e non 100 per riservare una cella al delimitatore di stringa '\0').

L'asterisco istruisce la fscanf() a consumare un carattere senza assegnarlo a nessuna variabile.