File di testo

Indice

Introduzione

File: sequenza di byte a cui il Sistema Operativo dà un nome ed una collocazione sul dispositivo di memoria di massa

classificazione per accesso

File ad accesso sequenziale

  • hanno record di lunghezza variabile
  • per accedere al record numero n bisogna leggere tutti i precedenti n -1 record per determinare il byte da dove esso inizia

File ad accesso diretto (casuale, random)

  • hanno record di lunghezza fissa
  • la lunghezza L del record viene decisa dal programmatore file per file
  • per accedere al record numero n si determina il byte da dove esso inizia con il calcolo: n *L

classificazione per contenuto

File di testo

  • Sono sequenze di caratteri organizzati in righe
  • Ogni riga è terminata da un ritorno a capo, l’ultima può non averlo
  • Le funzioni di I/O per stringhe e caratteri gestiscono il carattere '\n' come generico ritorno a capo

File binari

  • Sono sequenze “grezze” di byte
  • nessun carattere né sequenza di caratteri viene interpretata in modo speciale (fine riga, '\0', ecc.)

gestione file

Dal punto di vista del linguaggio C, non c’è differenza tra leggere da un file o dalla tastiera, o tra scrivere su un file o sul video. In ogni caso abbiamo due flussi (stream) di caratteri.

Le funzioni di I/O per i file richiedono che sia indicato lo stream da utilizzare, mentre quelle per video e tastiera utilizzano implicitamente degli stream associati a questi dispositivi

il tipo FILE

Per utilizzare un file è necessario che il sistema legga dal disco e tenga in memoria le sue caratteristiche (lunghezza, ecc.)

Queste informazioni sono memorizzate in una struttura dati composta (struct) che ha tipo FILE

Ogni accesso a un file avviene attraverso una variabile che si riferisce alla struttura FILE associata a quel file, questa variabile è di tipo puntatore ed è detta file pointer.

FILE *variabile;
FILE *fp;

La struttura dati FILE contiene tra le informazioni relative al file il cosiddetto offset (o file position pointer ) che memorizza il punto in cui la lettura o scrittura è arrivata (indica la posizione del prossimo byte da leggere o da scrivere, calcolata rispetto al primo byte del file che ha offset=0)

Apertura di un file

Per utilizzare un file bisogna richiederne l’accesso al Sistema Operativo indicando anche per quale scopo (lettura o scrittura), questa operazione è detta apertura del file

La funzione fopen apre un file

FILE *fp = fopen(nome, modo);

I parametri della funzione sono

nome
una stringa (costante o variabile) contenente il nome del file e l’eventuale percorso
modo
è una stringa indicante la modalità di apertura del file (lettura o scrittura)

fopen restituisce NULL se non riesce ad aprire il file, altrimenti restituisce il puntatore alla struttura FILE associata al file

Bisogna verificare sempre il valore di fopen

#include <stdio.h>
#include <stdlib.h>
#define NOME_FILE "prova.txt"

int main()
{
    FILE* fp;
    int i = 42;

    fp = fopen(NOME_FILE, "w");

    if (fp != NULL) {
        fprintf(fp, "SONO UNA CAROTA %d\n", i);
        fclose(fp);
    } else {
        printf("ERRORE SCRITTURA FILE\n");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

Chiusura di un file

Informa il Sistema Operativo che è terminato l’utilizzo di un certo file.

Funzione da usare:

fclose(fp);

Libera le risorse di sistema legate a quel file

  • È automatica al termine del programma (ma è buona norma chiuderlo non appena non serve più, in particolare se file di output)
  • Nel caso di file di output, prima della chiusura effettua automaticamente il flush dei buffer (vedere più avanti)

Lettura di un file

lettura riga per riga

Posso leggere un file una riga per volta facendo uso della funzione fgets e sfruttando il fatto che la funzione restituisce NULL quando non è più in grado di leggere dal file.

void leggo_file_una_riga_per_volta(FILE *fp)
{
    char s[LUNGHEZZA_RIGA]; // associo lunghezza massima di una riga ad 80 caratteri
    while(fgets(s, LUNGHEZZA_RIGA, fp) != NULL) {
        printf("Ho letto questa riga: ");
        printf("%s", s);
    }

    rewind(fp); // torno ad inizio file
}

lettura file carattere per carattere

In caso abbia la necessità di leggere un carattere alla volta devo fare uso della funzione fgetc

void leggo_file_un_carattere_alla_volta(FILE *fp)
{
    int val1;

    while((val1 = fgetc(fp)) != EOF) {
        printf("Ho letto il carattere -%c-\n", val1);
    }

    rewind(fp); // torno ad inizio file
}

lettura formattata

Le funzioni di lettura/scrittura su file sono varianti di quelle già viste per il terminale

int i = fscanf(fp, "%d%d%d", &val1, &val2, &val3);
// VARIANTE sscanf() legge da una stringa (es: parsing di una riga)
// EOF se il file non e' leggibile perche' sono arrivato in fondo
// i == 0 se riesco a leggere il file ma non ci sono NUMERI
// i == 1 se riesco a leggere un numero
// i == 2 se ne riesco a leggere 2
if (i == EOF) {
    printf("IL FILE E' FINITO\n");
} else {
    printf("i vale %d\n", i);
    printf("val1 vale %d\n", val1);
    printf("val2 vale %d\n", val2);
    printf("val3 vale %d\n", val3);
}

scrittura formattata

Le funzioni di lettura/scrittura su file sono varianti di quelle già viste per il terminale Per scrivere una stringa in un file e' possibile usare quindi la funzione fprintf

fprintf(fp, "%s", riga);

fprintf(fp, "%d - %d - %d\n", var1, var2, var3);

gestione errori

Gestione degli errori Le seguenti funzioni richiedono <stdio.h>

  • ferror(fp) dà un valore intero non nullo se si è verificato un EOF sullo stream fp
  • ferror(fp) dà un valore intero non nullo se si è verificato un errore (non EOF) sullo stream fp
  • clearerr(fp) cancella le indicazioni di errori e di EOF relativi a fp

gestione input formattato

Se la lettura di valori mediante fscanf produce un valore minore di quello atteso:

  • se feof è vera: raggiunta fine file
  • se ferror è vera: errore di lettura

altrimenti c’è un errore sul’la corrispondenza del tipo di dati (es. la stringa di formato ha un %d e il dato in input non è numerico)

if (fscanf(fp, "%d", &a) !=1) {
    if (feof(fp)) // fine del file
       // esci / segnala
    else if (ferror(fp)) // errore nel file
      // esci o segnala
    else
      // matching failure ( ho letto un intero ma c'era un stringa)
      fscanf(fp, "%*[^\n]"); // es. svuota la riga
}

Quando si verifica un errore, le funzioni che lavorano sui file (e altre) assegnano alla variabile errno (richiede <errno.h>), un valore intero che lo identifica

Per visualizzare il messaggio di errore corrispondente al valore di errno si usa la funzione perror(stringa) che visualizza stringa e un messaggio di errore corrispondente al valore in errno (serve includere <stdio.h> e <errno.h>)

Validate