Struttura dati FILE Concetto di FILE Per poter mantenere disponibili dei dati tra diverse esecuzioni di un programma (PERSISTENZA dei dati), è necessario poterli archiviare su memoria di massa. Un file è un astrazione fornita dal sistema operativo, il cui scopo è consentire la memorizzazione di informazioni su memoria di massa. Concettualmente, un file è una sequenza di registrazioni (record) uniformi, cioè dello stesso tipo. Un file è un astrazione di memorizzazione di dimensione potenzialmente illimitata (ma non infinita!), ad accesso sequenziale. Programmazione - Michele Colajanni, 2003/2004 387 1
Funzionamento Una testina di lettura/scrittura (concettuale) indica in ogni istante la registrazione (record) corrente. Inizialmente, la testina si trova per ipotesi sulla prima posizione. Dopo ogni operazione di lettura / scrittura, la testina si sposta poi sulla registrazione successiva. E illegale tentare di leggere / scrivere oltre la fine del file. Programmazione - Michele Colajanni, 2003/2004 388 Operare sui file A livello di sistema operativo, un file è denotato univocamente dal suo nome assoluto, che comprende il percorso e il nome relativo. A sua voltà, in certi sistemi operativi il percorso può comprendere il nome dell unità. In DOS e Windows: C:\temp\prova1.c In UNIX e Linux: /usr/temp/prova1.c Programmazione - Michele Colajanni, 2003/2004 389 2
Apertura file Poiché un file è un entità del sistema operativo, per agire su di esso dall interno di un programma occorre stabilire una corrispondenza fra: il nome del file come risulta al sistema operativo un nome di variabile definito all interno del programma Questa operazione si chiama APERTURA DEL FILE, ed è necessaria per collegare il mondo esterno (del sistema operativo) con l ambiente del programma in esecuzione Programmazione - Michele Colajanni, 2003/2004 390 Apertura e chiusura file Una volta aperto, il programma potrà poi agire sul file operando formalmente sulla variabile definita al suo interno: il sistema operativo provvederà ad effettuare realmente l operazione richiesta sul file associato a tale simbolo. Al termine, la corrispondenza dovrà essere eliminata mediante un operazione di CHIUSURA DEL FILE Questa operazione garantisce che tutte le modifiche apportate sul file siano rese effettive a livello di disco Programmazione - Michele Colajanni, 2003/2004 391 3
File in C Il modello di coordinazione della Standard I/O Library è basato sul concetto di file. Per gestire la corrispondenza fra il nome del file (come risulta al sistema operativo) ed una variabile del programma che rappresenta tale file, la Standard I/O Library definisce un apposito tipo: il tipo FILE. In pratica, FILE è una struttura definita all interno dello header standard stdio.h, che l utente non ha necessità di conoscere nei dettagli e che spesso cambia da un compilatore all altro! Programmazione - Michele Colajanni, 2003/2004 392 File in C (cont.) Le strutture di tipo FILE non sono mai gestite direttamente dall utente, ma solo ed esclusivamente mediante le funzioni della Standard I/O Library Nei suoi programmi, l utente dovrà solo definire, ove necessario, dei puntatori a FILE Il modello di coordinazione della Standard I/O Library è basato sul concetto di file, e distingue fra: file di testo sequenza di caratteri file binario sequenza di byte Programmazione - Michele Colajanni, 2003/2004 393 4
Apertura di un file Per aprire un file, stdio.h dichiara la funzione fopen(): FILE* fopen(char fname[ ], char modo[ ]) Questa funzione apre il file di nome fname nel modo specificato, e restituisce un puntatore a FILE (che punta a una nuova struttura FILE appositamente creata). ATTENZIONE alle convenzioni dipendenti dal sistema operativo usato (es., \ nei percorsi oppure /) La stringa modo specifica come aprire il file: r apertura in lettura (read) w apertura in scrittura (write) a apertura in aggiunta (append) seguita opzionalmente da: t apertura in modalità testo (default) b apertura in modalità binaria Programmazione - Michele Colajanni, 2003/2004 394 Apertura di un file (cont.) Il valore restituito da fopen() è un puntatore a FILE, da usare in tutte le successive operazioni sul file. Tale puntatore è NULL in caso in cui l apertura sia fallita (vi sono molte possibilità in tal senso) IMPORTANTE prima di usare il file, controllare sempre il risultato di fopen() Programmazione - Michele Colajanni, 2003/2004 395 5
Esempi (apertura file testo) Apertura di un file di testo in lettura: FILE *f; f = fopen("nomefile.txt","r"); Apertura di un file di testo in scrittura (= CREAZIONE di un file) FILE *f; f = fopen("nomefile.txt","w"); Apertura di un file di testo in aggiunta (in coda): FILE *f; f = fopen("nomefile.txt","a"); NOTA: Specificatore di modo "t" (testo), essendo di default, di solito si omette Programmazione - Michele Colajanni, 2003/2004 396 FILE di testo 6
Operazioni sui file di testo Per operare sui file di testo, la libreria standard fornisce funzioni del tutto analoghe a quelle già viste per i canali di I/O predefiniti: Input/Output a caratteri Input/Output a stringhe di caratteri Input/Output formattato A queste si aggiungono altre funzioni di utilità generale. Programmazione - Michele Colajanni, 2003/2004 398 Funzioni di I/O per file di testo Funzioni console Funzioni per file int getchar(void); int fgetc(file* f); int putchar(int c); int fputc(int c, FILE* f); char* gets(char* s); char* fgets(char* s, int n, FILE* f); int puts(char* s); int fputs(char* s, FILE* f); int printf(char* fm, int fprintf(file* f, char*fm, expr1,, exprn); expr1,...exprn); int scanf(char* fm, int fscanf(file* f, char* fm, addr1,...addrn); addr1,...addrn); Programmazione - Michele Colajanni, 2003/2004 399 7
Funzioni di I/O per file di testo (cont.) In particolare, getchar() fgetc(stdin) putchar(c) fputc(c,stdout) Altre funzioni: feof(), che indica se si è incontrato l EOF fseek(), che sposta la testina di lettura/scrittura in una posizione a scelta nel file ftell(), che dà la posizione corrente della testina di lettura/scrittura nel file CHIUSURA DI UN FILE Alla fine, per chiudere un file si usa fclose(): int fclose(file*); Programmazione - Michele Colajanni, 2003/2004 400 Esercizio 1 Si archivi ciò che viene inserito da console (digitandolo sulla tastiera) su un file di testo. #include <stdio.h> main() { FILE *fp; fp = fopen("prova.txt","w"); if (fp==null) printf("errore di apertura del file\n"); else { int c; while ((c=getchar())!=eof) fputc(c,fp); fclose(fp); ATTENZIONE: fopen() restituisce un puntatore NULL in caso di errore nell apertura del file (ad esempio, se non c è spazio su disco o il disco èprotetto) Programmazione - Michele Colajanni, 2003/2004 401 8
Esercizio 2 Stampare a video il contenuto di un file di testo. #include <stdio.h> main(){ FILE *fp; fp = fopen("prova.txt","r"); if (fp==null) printf("errore di apertura del file\n"); else { int c; while ((c=fgetc(fp))!=eof) putchar(c); fclose(fp); Programmazione - Michele Colajanni, 2003/2004 402 Esercizio 3 Leggere da un file di testo dati.txt una sequenza di numeri interi di al più 100 elementi finché non si trova il primo elemento uguale a 0 (si ipotizza che un elemento uguale a 0 esista). Memorizzare tutti i numeri (escluso lo 0) in un vettore. continua Programmazione - Michele Colajanni, 2003/2004 403 9
main() { int vett[100], temp, i; FILE *puntf; puntf = fopen("dati.txt","r"); /* Attenzione al percorso del file*/ if (puntf==null) printf("errore di apertura file\n"); else { i=0; fscanf(puntf, "%d", &temp); while (temp!=0) { vett[i]=temp; fscanf(puntf, "%d", &temp); i++; fclose(puntf); Programmazione - Michele Colajanni, 2003/2004 404 Altre versioni ESERCIZIO 3.b: Leggere da un file di testo dati.txt una sequenza di numeri interi terminata da 0. Memorizzare in un vettore tutti i numeri negativi. ESERCIZIO 3.c: Leggere da un file di testo dati.txt una sequenza di numeri interi terminata da 0. Memorizzare in un vettore tutti i numeri compresi tra 30 e +30 escluso lo 0. Ordinare il vettore in modo crescente e stampare tutti i numeri positivi. ESERCIZIO 3.d: Leggere da un file di testo dati.txt una sequenza di caratteri terminata da *. Memorizzare in un vettore tutti i caratteri alfabetici. Programmazione - Michele Colajanni, 2003/2004 405 10
Esercizio 4 Scrivere in un file di testo valori_pos.txt tutti i numeri positivi di un vettore di 100 interi e terminare con uno 0. main() { int vett[100], temp, i; FILE *puntf; puntf = fopen("valori_pos.txt","w"); if (puntf==null) printf("errore di apertura file\n"); else { for (i=0; i<100; i++) if vett[i]>0 fprintf(puntf, "%d\n", vett[i]); fprintf(puntf, "%d\n", 0); fclose(puntf); Programmazione - Michele Colajanni, 2003/2004 406 Altre versioni ESERCIZIO 4.b: Scrivere in un file di testo carat.txt tutti i caratteri di una stringa letta da input. Terminare la sequenza di caratteri del file con *. ESERCIZIO 4.c: Leggere da un file di testo dati_inp.txt una sequenza di numeri interi terminata da 0. Ordinare il vettore in modo crescente. Copiare tutti i valori positivi del vettore in un file di testo dati_out.txt. ESERCIZIO 4.d: Come l esercizio 4.c. In più, stampare su schermo il contenuto del file dati_out.txt. [ Ricordare l uso di EOF: while (fscanf(puntf, %d,&temp)!=eof) printf(..); ] Programmazione - Michele Colajanni, 2003/2004 407 11
FILE binari File binari Mentre il file di testo è una sequenza di caratteri, un file binario è una pura sequenza di byte Viene usato in C per archiviare su memoria di massa qualunque tipo di informazione che non sia un testo, in particolare: valori numerici (int, float, double, ) tipi definiti dall utente (struct, vettori, ) una copia della memoria del calcolatore Non c è EOF, perché un file binario non è una sequenza di caratteri Programmazione - Michele Colajanni, 2003/2004 409 12
Operazioni su file binari Per operare sui file binari, la libreria standard fornisce due funzioni: fread(), per leggere una sequenza di byte fwrite(), per scrivere una sequenza di byte Programmazione - Michele Colajanni, 2003/2004 410 fwrite() int fwrite(addr, int dim, int n, FILE *f); Scrive sul file n elementi, ognuno grande dim byte complessivamente, scrive quindi n dim byte Gli elementi da scrivere vengono prelevati in memoria a partire dall indirizzo addr Restituisce il numero di elementi (non di byte!) effettivamente scritti (possono anche essere meno di n). Programmazione - Michele Colajanni, 2003/2004 411 13
fread() int fread(addr, int dim, int n, FILE *f); Legge dal file n elementi, ognuno grande dim byte (complessivamente, tenta quindi di leggere n dim byte) Gli elementi da leggere vengono posti in memoria a partire dall indirizzo addr Restituisce il numero di elementi (non di byte!) effettivamente letti: controllare il valore restituito è il solo modo per sapere quanto è stato letto, e in particolare per scoprire se il file è finito. Programmazione - Michele Colajanni, 2003/2004 412 Esercizio 1 Salvare su un file binario il contenuto di un vettore di interi. #include <stdio.h> #include <stdlib.h> main(){ FILE *fp; int vet[10] = {1,2,3,4,5,6,7,8,9,10; if ((fp = fopen("numeri.dat","wb"))==null) { fprintf(stderr, "Errore di apertura \n"); exit(1); fwrite(vet,sizeof(int),10,fp); fclose(fp); Programmazione - Michele Colajanni, 2003/2004 413 14
Note Essendo vet il nome di un vettore, rappresenta già un indirizzo, quindi non occorre ricavarlo con l operatore & L operatore sizeof() è essenziale perché il programma sia portabile da una piattaforma all altra Stavolta è necessario mettere lo specificatore di modo b (binario) Si sarebbe anche potuto fare un ciclo di dieci fwrite(), una per ogni int da scrivere, ma sarebbe stato inutilmente inefficiente (ma necessario nell eventualità di uso di file di testo) Programmazione - Michele Colajanni, 2003/2004 414 Esercizio 2 Leggere da un file binario 40 numeri interi e memorizzarli in un vettore. #include <stdio.h> #include <stdlib.h> main(){ FILE *fp; int vet[40], i, n; if ((fp = fopen("archivio.dat","rb"))==null) { fprintf(stderr, "Errore di apertura\n"); exit(1); n = fread(vet,sizeof(int),40,fp); for (i=0; i<n; i++) printf("%d ",vet[i]); fclose(fp); Programmazione - Michele Colajanni, 2003/2004 415 15
Note Sebbene fread() tenti di leggere 40 interi (il massimo compatibile con le dimensioni del vettore), potrebbe leggerne meno se il file finisse prima (raggiungimento dell EOF) è importante considerare il valore restituito, n Si sarebbe anche potuto fare un ciclo di dieci fread(), una per ogni int da leggere, ma sarebbe stato inutilmente inefficiente (ma necessario nell eventualità di uso di file di testo) Programmazione - Michele Colajanni, 2003/2004 416 File Binari o File Di Testo? Riprendiamo le due definizioni: file di testo sequenza di caratteri file binario sequenza di byte Poiché un carattere in C è ampio esattamente un byte, ci si può chiedere quale sia realmente la differenza fra i due tipi di file. In un file binario si potrebbero anche archiviare testi, ma solitamente non conviene farlo poiché le funzioni disponibili per l I/O da file di testo sono molto più sofisticate i file di testo non sono indispensabili, ma più comodi! Programmazione - Michele Colajanni, 2003/2004 417 16
Esercizio 3 Scrivere un testo su un file binario e poi rileggerlo #include <stdio.h> #include <string.h> main(){ FILE *fp; char msg[]= L esame si avvicina, buf[25]; int n; if ((fp = fopen( prova.dat", wb"))==null) { fprintf(stderr, "Errore di apertura\n"); exit(1); fwrite(msg,strlen(msg)+1,1,fp); fclose(fp); //apertura in scrittura Programmazione - Michele Colajanni, 2003/2004 418 Esercizio 3 if ((fp = fopen( prova.dat", rb"))==null) { //apertura in lettura fprintf(stderr, "Errore di apertura\n"); exit(1); n = fread(buf, strlen(msg)+1, 1, fp); buf[n] = '\0'; printf("%s\n", buf); fclose(fp); NOTE: occorre gestire in prima persona la lunghezza della stringa, compreso il terminatore Programmazione - Michele Colajanni, 2003/2004 419 17
Esercizio 4 Archiviare in un file binario una serie di dati relativi a persone (ogni persona sia caratterizzata da cognome, nome ed età). #include <stdio.h> #include <stdlib.h> typedef struct { char cognome[20], nome [20]; int eta; persona; main(){ FILE *fp; persona p; fp = fopen("archivio.dat","ab"); if (fp==null) { fprintf(stderr, "Errore di apertura\n"); exit(1); Programmazione - Michele Colajanni, 2003/2004 420 Esercizio 4 do { printf("immettere Cognome, Nome ed età:\t"); scanf("%s%s%d", p.cognome, p.nome, &p.eta); if (eta>=0) fwrite(&p,sizeof(persona),1,fp); while (eta>=0); fclose(fp); NOTA: poiché p è una variabile di tipo persona (non un indirizzo!), occorre ricavare il suo indirizzo con l operatore & Programmazione - Michele Colajanni, 2003/2004 421 18
Esercizio 5 Leggere da un file binario e mostrare a video i dati relativi alle persone presenti nel file (il tipo persona è dato). #include <stdio.h> #include <stdlib.h> typedef struct { char cognome[20], nome [20]; int eta; persona; main(){ FILE *fp; persona p; fp = fopen("archivio.dat","rb"); if (fp==null) { fprintf(stderr, "Errore di apertura\n"); exit(1); Programmazione - Michele Colajanni, 2003/2004 422 Esercizio 5 while (fread(&p,sizeof(persona),1,fp)>0) printf("cognome: %s, Nome: %s, Età:%d\n", p.cognome, p.nome, p.eta); fclose(fp); NOTE: sebbene fread() tenti di leggere una struttuta persona, potrebbe non leggerne nessuna se il file finisse prima il ciclo termina quando fread() restituisce 0 è essenziale che la typedef sia identica a quella usata quando il file è stato scritto: altrimenti, si leggeranno solo sequenze di byte senza senso Programmazione - Michele Colajanni, 2003/2004 423 19