Lettura di un file per righe

La funzione fscanf permette di leggere tutti i dati di tipo scalare, ossia interi, caratteri e reale. Permette inoltre di leggere stringhe, utilizzando il formato %s. Leggere una stringa in questo modo può a volte portare a risultati che non ci si aspetta: la funzione fscanf, infatti, considera una stringa in input conclusa quando incontra uno spazio.

In alcuni casi può essere necessario leggere invece una intera riga da file. Per questa ragione, è stata introdotta la funzione fgets. L'uso tipico di questa funzione è quello di leggere una intera riga da un file di testo, e poi suddividere questa linea usando la funzione di lettura da stringhe sscanf che si vedrà più avanti. Questa funzione viene anche usata quando una linea in ingresso va considerata come una sola stringa, su cui poi si opera direttamente con le funzioni su stringhe.

La funzione fgets ha tre argomenti: il primo è un vettore di caratteri in cui va memorizzata la linea del file di testo; il secondo è il numero massimo di caratteri che si vogliono mettere in questa stringa; il terzo è un descrittore di file, e indica da quale file si vuole leggere la stringa. Il prototipo di questa funzione è il seguente:

    char *fgets(char *s, int size, FILE *fd);

Diamo una descrizione degli argomenti e del valore di ritorno di questa funzione:

char *s
questo è un puntatore a una zona di memoria in cui viene memorizzata la stringa letta; si noti che la zona di memoria deve già essere stata allocata, o staticamente oppure usando malloc; in altre parole la funzione fgets non alloca la memoria in cui mettere la stringa: passare alla funzione un puntatore non inizializzato è un errore;
int size
il numero memorizzato in questa variabile dice alla funzione quale è la dimensione della zona di memoria puntata da s; in questo modo, la funzione sa che non può scrivere nella zona di memoria più di size caratteri, altrimenti finirà per scrivere dei valori in zone di memoria al di fuori di quella allocata per s; la funzione non scrive mai più di size caratteri, anche se la linea del file è più lunga;
FILE *fd
questo è il descrittore di un file, e indica da quale file bisogna leggere
valore di ritorno
coincide con il primo argomento della funzione (s) se è stato letto almeno un carattere (incluso il ritorno a capo); altrimenti ritorna NULL.

Capire quando il file è finito è semplice: basta infatti verificare se il valore risultato vale NULL. Se il risultato è NULL, allora non è stato possibile leggere neanche un carattere. In questo caso, la zona di memoria puntata da s non contiene un valore significativo, per cui non va elaborata.

Esistono ovviamente casi in cui viene letta una linea da file ``ogni tanto'', ma di solito la funzione fgets viene usata per leggere tutto il contenuto di un file riga per riga. Su ogni riga vengono poi fatte delle elaborazioni.

La struttura di un programma di questo genere è: prima si apre il file in lettura (con controllo errori), e poi si entra in un ciclo, in cui si legge una riga a ogni iterazione. In ogni iterazione, si legge una riga, e la si elabora. Se l'operazione di lettura ha dato risultato NULL allora vuol dire che non è stata letta nessuna riga, per cui s non va elaborata, e si deve invece uscire dal ciclo.

  1. apri il file
  2. controllo errore in apertura
  3. ciclo:
    1. leggi riga
    2. se non è stata letta la riga, esci
    3. elabora le riga
  4. chiudi il file

Il programma righe.c riportato qui sotto legge un file riga per riga. La elaborazione di una riga è in questo caso semplicemente le sua stampa su schermo. Si noti che la funzione fgets mette nel vettore s tutta la riga letta, incluso il carattere di fine linea. È per questo che la istruzione di stampa usa il formato "%s" invece che "%s\n": infatti, il carattere di andata a capo si trova già nella stringa letta da file.

/*
  Lettura di un file riga per riga.
*/

#include<stdlib.h>
#include<stdio.h>

int main() {
  FILE *fd;
  char buf[200];
  char *res;


		/* apre il file */
  fd=fopen("righe.txt", "r");
  if( fd==NULL ) {
    perror("Errore in apertura del file");
    exit(1);
  }


		/* legge e stampa ogni riga */
  while(1) {
    res=fgets(buf, 200, fd);
    if( res==NULL )
      break;
    printf("%s", buf);
  }


		/* chiude il file */
  fclose(fd);

  return 0;
}

Si faccia attenzione al fatto che il valore di ritono della funzione fgets (che serve per capire quando il file è finito) è di tipo puntatore a carattere (ossia char *), e non intero come nel caso di fscanf. Questo puntatore non deve ovviamente venire inizializzato.