edutecnica

Files e stream

     

In C++ i programmi comunicano con i dispositivi periferici mediante appositi canali virtuali attraverso i quali scorre il “flusso” (stream) di informazioni.
Quando inizia l'esecuzione di un programma vengono automaticamente aperti quattro stream standard, uno di input, cin, associato alla tastiera, e tre di output, cout, cerr e clog, collegati con lo schermo. Per accedere ai files su disco la Libreria Standard dispone di altri tipi di stream: ifstream (input), ofstream (outpuf) e fstream (input/output).

Apertura di un file

     

Per leggere e scrivere dei dati su un file occorre creare uno stream adeguato e associarlo al file sul disco.
Questa associazione è denominata “apertura del file”.

// Stream per l’input da file
ifstream dati;
// Apertura del file iscritti.txt
dati.open( "iscritti.txt " );

In alternativa è possibile dichiarare e aprire il file con un'unica istruzione:

ifstream dati( "iscritti.txt" );

Se l'operazione di apertura è andata a buon fine lo stream assume il valore true, altrimenti false.
I tipi di stream che consentono l'accesso ai file sono definiti nell' header file <fstream>.

Modalità di apertura di un file

     

Quando si crea uno stream associato a un file occorre stabilire la direzione del flusso di dati. Le modalità previste sono:

Apertura in lettura
è la modalità predefinita per gli stream di tipo ifstream;
il file associato deve esistere;
per impostazione predefinita la lettura comincerà all’ inizio del file.

Apertura in scrittura
è la modalità predefinita per gli stream di tipo ofstream;
il file associato viene creato. Se esiste già, il contenuto precedente è cancellato;
i dati sono scritti apartire dall’inizio del file.

Apertura in append
il file associato viene creato. Se esiste già, il contenuto precedente è preservato;
i nuovi dati sono aggiunti alla fine del file.

Modalità di accesso ai files

     

Le modalità di accesso a un file possono essere specificate al momento della sua apertura mediante degli enumeratori predefiniti. I principali sono:

ios::in : accesso in lettura;
ios::out : accesso in scrittura;
ios::app : accesso in modalità append;
ios::binary : apertura di un file binario.

Gli enumeratori possono essere combinati mediante l'operatore bit a bit OR (|) per specificare modalità di apertura personalizzate.

Esempio:



// Apertura in lettura
fstream in("dati", ios::in);
// Apertura in lettura/scrittura
fstream in_out( "dati",ios::in | ios::out);
// Apertura in modalità append
fstream app("dati", ios::app);

Chiusura di un file

     

Quando uno stream non occorre più è possibile chiuderlo, ovvero disconnetterlo dal file cui era associato. La chiusura avviene mediante il metodo close().

Esempio:



dati.close();

Durante la chiusura di uno stream aperto in scrittura, i caratteri presenti nel buffer interno vengono immediatamente scritti nel file associato.
Gli stream vengono chiusi automaticamente alla fine del programma o all'uscita del blocco in cui sono dichiarati.
Una volta chiuso, io stesso stream può essere riaperto con qualunque modalità (compatibile al suo tipo) e associato a qualunque file.

Files di testo

     

Sono file che solitamente contengono solo caratteri visualizzabili direttamente sullo schermo.
Possono essere aperti e modificati da qualunque programma editor. I dati scritti su questi file sono convertiti nei corrispondenti caratteri.
Per esempio, il numero reale 1.23 è memorizzato come la sequenza di caratteri '1,' '.' '2' '3' .

Files binari

     

Sono file che hanno 1o scopo di memorizzare qualsiasi tipo di infomazione.I dati scritti in questi file sono registrati così come sono rappresentati in memoria, senza nessuna conversione o formattazione.
Spesso i file binari sono più compatti degli equivalenti file di testo e consentono prestazioni migliori. Hanno pero lo svantaggio di essere meno portabili e il loro listato normalmente è privo di significato.

Accesso a files di testo

     

Per leggere e scrivere su un file di testo si possono impiegare gli stessi operatori e le stesse modalità usati per gli stream di ingresso e di uscita standard cin e cout.

// Legge dallo stream f_in
f_in >> articolo >> prezzo;
// Legge un'intera riga da f_in
f_in.getline(riga, 80);
// Legge un solo carattere
f_in.get(c);

// Scrittura sullo stream f_out
f_out << tempo << spazio << endl;
// Scrittura di un carattere
f_out.put(c);

Errori di lettura

     

Se durante la lettura di un file si verifica un errore, per esempio quando si cerca di leggere una sequenza di caratteri che non è possibile convertire nel tipo di variabile corrispondente, lo stream assume il valore false:

dati >> articolo >> prezzo;
if(!dati)
cout << "Errore! " << endl;

In questi casi la prima operazione da compiere è ripristinare lo stato normale dello stream mediante il metodo clear():

dati.clear();

In seguito si devono "saltare" i caratteri che hanno causato l'errore, per esempio mediante il metodo ignore():

dati.ignore(10000,'\n') ;

Questa istruzione consente di "ignorare" 10000 caratteri o almeno tutti quelli fino al prossimo NEW-LINE compreso.

Accesso a files binari

     

Un file binario deve essere aperto specificando l'enumeratore predefinito ios::binary.
Per scrivere in un file binario s'impiega il metodo write(), per leggere il metodo read().
Per entrambi occorre specificare il "blocco" di byte che deve essere registrato o letto, ovvero l’indirizzo iniziale e la sua dimensione.

int peso = 70;
// Scrittura in un file binario
f_out.write((char *)&peso,sizeof peso);

// Lettura da un file binario
f_out.read((char *)&peso,sizeof peso);

Il cast a char * dell'indirizzo del dato da leggere o scrivere è necessario.

Verifica della fine del file

     

Quando si leggono dei dati da un file non ha ovviamente più senso effettuare altre letture dopo la sua fine.
Per verificare questa condizione è previsto il metodo eof() (end of file), che assume il valore true dopo che è stato letto l'ultimo carattere disponibile nello stream.

esempio:



do{
   dati >> articolo >> prezzo;
   if(dati)
       cout<< articolo << '\t'<<< endl;
}while( !dati.eof());

Lo stato di "fine" file va verificato sempre dopo la lettura dei dati, mai prima.

Accessi sequenziali

     

La posizione da cui leggere o in cui scrivere i dati in uno stream è definita da un puntatore interno che, a ogni accesso, viene incrementato automaticamente della dimensione del dato letto o scritto. Questo meccanismo consente un accesso predefinito di tipo sequenziale, i dati sono letti o scritti consecutivamente.

Accessi random

     

L'indicatore di posizione interno degli stream si può spostare impiegando il metodo seekg() per quelli di lettura e seekp() per quelli di scrittura. In questo modo il programmatore può stabilire quale sarà il prossimo dato da leggere o in quale posizione scriverlo ottenendo, pertanto, un accesso di tipo random.
Lo spostamento del puntatore può essere assoluto oppure relativo rispetto l'inizio del file, alla sua fine o alla posizione corrente:

// Punta al 101 byte
dati.seekg(100);

// Punta 10 byte dopo l'inizio
dati.seekg(10, ios::beg) ;

// Punta 4 byte prima della fine
dati.seekg(-4, ios::end) ;

// Punta 8 byte dopo
dati.seekg(8, ios::cur);

ios::beg, ios::end e ios::cur, sono degli enumeratori predefiniti.