stdio.h
, la quale sta per STandarD Input/Output.
Il primo tipo di cui abbiamo bisogno è il tipo FILE
, che è anomalo rispetto ai tipi che abbiamo fino ad ora incontrato; il motivo risiede nel fatto che la gestione dei files dipende pesantemente dal sistema operativo utilizzato, per cui si è preferito utilizzare dei meccanismi interni di memorizzazione delle variabili di tipo FILE
, piuttosto che creare una struttura o qualcosa del genere. Comunque non dobbiamo preoccuparci: basta trattare le variabili di tipo FILE
diversamente dalle altre, cioè non bisogna provare ad allocarle manualemente ma semplicemente far sì che siano le funzioni di libreria a svolgere questo compito per contro nostro. Le prime funzioni che incontriamo sono le seguenti:
FILE* fopen (const char* nome_file, const char* modalita)
int fclose (FILE* nome_file) |
fclose
è molto semplice: essa chiude un file precedentemente aperto e torna 0
se l'operazione è riuscita; in caso fclose
fallisca nel chiudere un file essa torna il valore EOF
, definito in stdio.h
, il quale sta per End Of File (trad. ``fine del file''). Se la funzione fclose
non viene chiamata mai all'interno del programma, alla fine di esso vengono automaticamente chiusi tutti i files aperti; tale operazione viene effettuata anche nel caso il programma venga chiuso tramite la nota funzione exit
(contenuta nella stdlib.h
), ma non nel caso si verifichino errori di esecuzione o si chiami la funzione abort
(contenuta anch'essa nella stdlib.h
), la quale termina immediatamente il programma in esecuzione.
La funzione fopen
apre un file; cosa vuol dire ``aprire un file''? Significa che al file chiamato nome_file 8.7 viene associato uno stream (trad. ``flusso'') sul quale è possibile operare in modalità di scrittura o di lettura con le funzioni che presto vedremo. La modalità di apertura di un file è determinata dall'argomento modalita:
r |
sola lettura (reading-only) | |
w |
sola scrittura (writing-only) | |
a |
concatenazione (append) | |
r+ |
lettura/scrittura | |
w+ |
scrittura/lettura |
La modalità r
consente esclusivamente di leggere un file; se l'operazione di apertura del file fallisce, ad esempio perché il file non esiste, viene tornato un puntatore nullo. La modalità w
consente esclusivamente di scrivere su nome_file; se esso non esiste viene creato, altrimenti il file preesistente viene distrutto. Anche aprendo un file in modalità a
si può solamente scrivere su di esso, tuttavia se il file esiste, la scrittura si effettua alla fine di esso: lo stream viene cioè concatenato al file preesistente. Le modalità r+
e w+
consentono entrambe di leggere e scrivere contemporaneamente su di un file; la differenza tra le due è che nel caso nome_file esista prima dell'apertura, con la prima modalità il file resta invariato mentre con la seconda esso viene distrutto.
Vediamo un esempio che determina l'esistenza di un file; nel caso esso non esista viene creato un file vuoto.
// ex8_6_1 #include <iostream.h> #include <stdio.h> void main() { char f[50]; cout << "nome file? "; cin >> f; FILE* file; if ( (file = fopen (f, "r")) != 0 ) cout << "il file " << f << " esiste\n"; else file = fopen (f, "w"); fclose (file); }
esempio di output:
nome file? prova
esempio di output:
nome file? prova
il file prova esiste
Supponiamo che nella cartella contenente ex8_6_1.cpp
non ci sia nessun file chiamato ``prova''; se eseguiamo due volte il programma di esempio precedente, la prima volta il file non esiste, dunque viene creato, poi viene segnalata la sua esistenza.
Aprire e chiudere i files può essere interessante, ma abbastanza inutile; vediamo invece come leggere e scrivere su files aperti:
int putc (int c, FILE* stream)
int fputs (const char* s, FILE* stream) int getc (FILE* stream) |
Le prime due funzioni, putc
e fputs
, servono semplicemente a scrivere su stream; la prima di esse scrive un carattere (c), la seconda una stringa (s) omettendo il carattere di fine stringa '\0'
. Entrambe tornano il valore costante EOF
in caso di fallimento, un valore positivo in caso di successo. Vediamo un semplice esempio:
// ex8_6_2 #include <iostream.h> #include <stdio.h> #include <string.h> void main() { FILE* stream = fopen ("prova", "w"); // NOTA: se `prova' esiste viene cancellato cout << "scrivi 'esci' per terminare\n"; char buffer[100]; for ( ; ; ) { // infinite loop cout << "? "; cin >> buffer; if ( strcasecmp(buffer, "esci") == 0 ) break; int codiceRitorno = fputs (buffer, stream); // aggiunge il carattere di fine riga putc ('\n', stream); if (codiceRitorno == EOF) { cout << "ERRORE DI SCRITTURA!\n"; exit(1); } } fclose (stream); }
esempio di output:
scrivi 'esci' per terminare
? love
? is
? sentimental
? measles
? esci
Se si prova ad aprire il file `prova' si vedrà che esso contiene esattamente:
love
is
sentimental
measles
La funzione più semplice per la lettura di uno stream è getc
; essa non fa altro che leggere un carattere da stream e restituirlo. Vediamo un esempio che ci permette di copiare un file in un altro:
// ex8_6_3 #include <iostream.h> #include <stdio.h> #include <stdlib.h> void main() { char fileSorgente[50]; char fileDestinazione[50]; cout << "sorgente? "; cin >> fileSorgente; cout << "destinazione? "; cin >> fileDestinazione; FILE* streamSorg = fopen(fileSorgente, "r"); // controlla che il file sorgente esista if (streamSorg == 0) { cout << "errore in fase di apertura " "di " << fileSorgente << "\n"; exit (1); } FILE* streamDest = fopen(fileDestinazione, "w"); char c; while ((c = getc (streamSorg)) != EOF ) putc (c, streamDest); fclose (streamSorg); fclose (streamDest); }
esempio di output:
sorgente? file_che_non_esiste
destinazione? file_di_destinazione
errore in fase di apertura di file_che_non_esiste
Se il file fornito come sorgente esiste, otterremo una copia esatta di esso nel file di destinazione; possiamo costruire un nostro comando di copia di un file utilizzando gli argomenti da riga di comando:
// ex8_6_4 #include <iostream.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char** argv) { if (argc < 3) { cout << "la sintassi e`:" "\n\tcopia file_sorgente file_destinazione\n"; exit (1); } FILE* sorg = fopen (argv[1], "r"); FILE* dest = fopen (argv[2], "w"); // controlla che il file sorgente esista if (sorg == 0) { cout << "il file " << sorg << " non esiste!\n"; exit (1); } // copia `sorg' in `dest' char c; int n = 0; // conta i bytes copiati while ((c = getc (sorg)) != EOF ) { putc (c, dest); n++; } fclose (sorg); fclose (dest); cout << "copia di " << n << " effettuata\n"; return 0; }
Tale semplice programma copia byte a byte il file sorgente nel file destinazione, restituendo al termine dell'operazione il numero di bytes copiati.