Il file binario non è altro che una collezione di byte, memorizzata sul file system (ad esempio nell hard disk), che si può leggere sequenzialmente (ma non solo sequenzialmente). Un file di testo è prima di tutto un file binario. Le funzioni fscanf e fprintf, attraverso le stringhe di formato, non fanno altro che astrarre questa caratteristica. I file binari rimangono però necessari in molti casi. Esercitazioni di Fondamenti Informatica - Modulo A 1
P i p p o \0 \0 \0 \0 \0 0110 (int) P i p p o 2 3 4 5 6 7 8 9 \n Vogliamo per esempio memorizzare un nome e un numero di telefono: Andrea 34567890 I numeri sono codificati Le stringhe non possono essere lette come tali, come fermarsi? Tipicamente vengono scritti tutti gli N byte fisici dell array di char, compresa la parte inutile. Se cambio un nome non devo spostare tutto il contenuto del file per fare spazio Posso memorizzare dati che non hanno una codifica simbolica E immediata la codifica del testo Può essere quindi letto e modificato dagli editor di testo I numeri sono scritti come stringhe e quindi ad ogni cifra corrisponde un carattere, per numeri grandi possono occupare molti byte Se devo cambiare una stringa devo fare uno shift di tutto il file Esercitazioni di Fondamenti Informatica - Modulo A 2
P i p p o \0 \0 \0 \0 \0 0110 10 (int) Analogamente ai file di testo si usa la funzione fopen FILE* fopen (const char *filename, const char *mode); Però è necessario specificare nella stringa mode che si tratta di un file binario: wb" binario in scrittura rb" binario in lettura ab" binario in modalità append f=fopen ("filebinario.bin", rb"); //PRIMA r E POI b!! Se f==null allora c è stato un problema durante l apertura (file inesistente se in lettura, file protetto se in scrittura ed altri possibili problemi). In questo caso si potrebbe chiudere il programma. Esercitazioni di Fondamenti Informatica - Modulo A 3
P i p p o \0 \0 \0 \0 \0 0110 10 (int) #include<stdio.h> #include<stdlib.h> //qui è dichiarata la funzione exit main() { FILE *f; f=fopen ("filebinario.bin", rb"); if(f==null) { printf("errore nell apertura del file. Uscita dal programma\n"); exit(-1); } } Esercitazioni di Fondamenti Informatica - Modulo A 4
#include<stdio.h> #include<stdlib.h> // contiene la funzione exit FILE * apri_file(char filename[], char mode[]) { FILE *f; printf("apro il file: %s\n", filename); f=fopen (filename, mode); P i p p o \0 \0 \0 \0 \0 0110 10 (int) if(f==null) { printf("errore nell apertura del file %s\n", filename); printf("uscita dal programma\n"); exit(-1); } } return f; main() { FILE *f; f=apri_file("filebinario.bin","rb"); // } Esercitazioni di Fondamenti Informatica - Modulo A 5
P i p p o \0 \0 \0 \0 \0 0110 10 (int) size_t fwrite ( const void *ptr, size_t size, size_t count, FILE * stream ); size_t fread ( void *ptr, size_t size, size_t count, FILE * stream ); size_t size non è altro che un intero positivo e indica il numero di byte che compongono il singolo elemento size_t count è il numero di elementi che voglio scrivere o leggere void *ptr è un puntatore ad un'area di memoria, da lì inizio a leggere o scrivere una serie di byte in modo sequenziale. Quanti?? Se tutto va bene vengono letti/scritti count*size byte. ptr è un puntatore a void, in modo tale che possa scrivere o leggere qualsiasi tipo di dato. Siccome la dimensione del singolo elemento non è nota al compilatore allora devo passare tale valore come parametro (size_t size). Le funzioni restituiscono il numero di elementi letti o scritti, se tale quantità è inferiore a count vuol dire che c è stato un problema Esercitazioni di Fondamenti Informatica - Modulo A 6
0110 10 (int) 0011 01 (int) Scrittura e lettura di un singolo elemento: int numero; r = fread (&numero, 4, 1, f); w = fwrite (&numero, 4, 1, f); Si noti che passo la grandezza dell intero numero come costante: 1. E se uso il programma in un architettura che usa interi da 2 byte? 2. E se ad un certo punto decido che la variabile numero è un double (8 byte)? Dovrei cambiare tutte le occorrenze di questa grandezza. Quindi è PERICOLOSO e da evitare. Si usa in questi casi l operatore sizeof che restituisce la dimensione del tipo o della variabile che gli passo, così si evita almeno la casistica di errore semantico 1. sizeof(int) [tipicamente 4, dipendente dal sistema utilizzato] sizeof(char) [tipicamente 1] Esercitazioni di Fondamenti Informatica - Modulo A 7
Scrittura e lettura di un singolo elemento, versione migliorata: int numero; r = fread (&numero, sizeof(int), 1, f); w = fwrite (&numero, sizeof(int), 1, f); 0110 10 (int) 0011 01 (int) Oppure int numero; r = fread (&numero, sizeof(numero), 1, f); w = fwrite (&numero, sizeof(numero), 1, f); In questo caso se cambio il tipo di numero non devo modificare anche le fread e le fwrite. Questa modalità però è più pericolosa quando si lavora con gli array. Si consiglia, quindi, di usare la prima modalità. Esercitazioni di Fondamenti Informatica - Modulo A 8
In modo analogo posso scrivere anche strutture definite da me: typedef struct { char nome[10]; char cognome[10]; int eta; } persona; P i p p o \0 \0 \0 \0 \0 D e P i p p i s \0 \0 0110 10 (int) persona p; w = fwrite (&p, sizeof(persona), 1, f); r = fread (&p, sizeof(persona), 1, f); Esercitazioni di Fondamenti Informatica - Modulo A 9
Scrittura e lettura di un array di interi: int interi[dim]; int ni; FILE *fileinputinteri, *fileoutputinteri; //... // Apro i file fileinputinteri = fopen("inputint.bin", "rb"); fileoutputoutput = fopen("outputint.bin", "wb"); // RICORDARSI DI CONTROLLARE LA CORRETTA APERTURA DEI FILE //... // Leggo DIM interi (dimensione FISICA dell array) // Se il file contiene meno di DIM interi ni contiene il // numeri di interi letti: // ottengo ni (dimensione LOGICA dell array) ni = fread (interi, sizeof(int), DIM, fileinputinteri); // scrivo ni interi fwrite (interi, sizeof(int), ni, fileoutputinteri); //... Esercitazioni di Fondamenti Informatica - Modulo A 10
Scrittura e lettura di un array di strutture: persona anagrafe[dim]; int na; FILE *fileinputanag, *fileoutputanag; //... // Apro i file fileinputanag = fopen("inputan.bin", "rb"); fileoutputanag = fopen("outputan.bin", "wb"); // RICORDARSI DI CONTROLLARE LA CORRETTA APERTURA //... // Leggo DIM interi (dimensione FISICA dell array) // Se il file contiene meno di DIM interi na contiene il // numeri di interi letti: // ottengo na (dimensione LOGICA dell array) na = fread (anagrafe, sizeof(persona), DIM, fileinputanag); // scrivo na interi fwrite (anagrafe, sizeof(persona), na, fileoutputanag); //... Esercitazioni di Fondamenti Informatica - Modulo A 11
NOTA: QUESTE PROSSIME 4 SLIDE SERVONO A CHIARIRE ALCUNI PROBLEMI RISCONTRATI NELL ESERCITAZIONE PRECEDENTE. NON SARANNO OGGETTO DI ESAME. "Io ne ho viste cose che voi umani non potreste immaginarvi: navi da combattimento in fiamme al largo dei bastioni di Orione e ho visto i raggi B balenare nel buio vicino alle porte di Tannhauser. E tutti quei momenti andranno perduti nel tempo, come lacrime nella pioggia. E' tempo di morire." La volta scorsa abbiamo riscontrato alcuni problemi leggendo testo simili a quello sopra riportato. Vediamo che meccanismo troviamo alla base della lettura di file di testo. Esercitazioni di Fondamenti Informatica - Modulo A 12
"Io ne ho viste cose che voi umani non potreste immagin arvi: navi da combattimento in fiamme al largo dei basti oni di Orione e ho visto i raggi B balenare nel buio vicin o alle porte di Tannhauser.\n E tutti quei momenti andranno perduti nel tempo, come lacrime nella pioggia.\n E' tempo di morire." Nell esercizio di crittografia, alcuni hanno deciso di leggere dal file usando la stringa di formato per le stringhe %s. In questo caso, fscanf filtra di default i caratteri spazio e a capo. Quindi fscanf(f, "%s", stringa); non legge l intera riga ma solo Qui, tralasciando lo spazio successivo, che viene perso. Per leggere anche gli spazi e gli a capo bisogna leggere i singoli caratteri (anche lo spazio e a capo sono caratteri): fscanf(f, "%c", &c); Esercitazioni di Fondamenti Informatica - Modulo A 13
Qui tutto cambia e s'espande\n com'è pura l'aria,\n radiosa la luce del giorno\n immenso il lontano orizzonte\n libertà, ridiscendi dai cieli\n che ricominci il tuo regno\n libertà libertà.\n Gioacchino Rossini da "Guglielmo Tell" fscanf(f, "%s", stringa); Le lettere accentate (ed altri caratteri speciali) non appartengono alla tabella ascii classica. Quindi non sono codificati con un solo byte, ma con 2 o più byte, che vengono letti come se fossero caratteri singoli. Quindi cosa c è dentro stringa? "com' \0" Poi, quando si riscrive stringa nel file di testo e aprite un editor viene visualizzata correttamente la lettera accetata. Esercitazioni di Fondamenti Informatica - Modulo A 14
Quando leggiamo da file di testo, quindi, dobbiamo prestare particolare attenzione a quale modalità di lettura per le parole usare: Se abbiamo bisogno di gestire ogni singolo carattere (compresi spazi, tabulazioni, ecc.) dovremo leggere carattere per carattere. Se ci basta avere le informazioni contenute nelle singole parole, senza tutte le tabulazioni e i caratteri di impaginazione, allora ci converrà eseguire letture di stringhe, che leggeranno parola per parola. Durante l esame vi verrà chiesto di leggere solo stringhe senza spazi o dati (interi, float, singoli caratteri) che possono essere letti con una scanf Esercitazioni di Fondamenti Informatica - Modulo A 15
Nel file di testo anagrafe.txt sono presenti personaggi storici ed attuali (Nome Cognome eta) Nome e Cognome sono stringhe lunghe al massimo 20 caratteri (compreso terminatore) Si legga l anagrafe testuale e, una persona alla volta, si salvi il contenuto nel file binario anagrafe.bin Si legga l anagrafe binaria e, una persona alla volta, si salvi il contenuto nel file di testo copia_anagrafe.txt. Esercitazioni di Fondamenti Informatica - Modulo A 16
Si scriva un programma che, letti due file binari ambito.bin e libretto.bin (descritti successivamente e forniti nella cartella dell esercitazione) e una stringa inserita da tastiera dell utente (indicante un ambito di studi), stampi su un secondo file binario solo gli esami appartenenti all ambito specificato dall utente; infine si legga e stampi a video il contenuto del file per verificare che la scrittura sia avvenuta correttamente. Il file binario ambito.bin è composto da righe nel formato <nome_esame> <ambito_esame>, mentre libretto.bin contiene righe nel formato <nome_esame> <voto>; <nome_esame> ha lunghezza massima 20 caratteri (più il terminatore), <ambito_esame> ha lunghezza massima 3 più il terminatore. L ordine degli esami nei 2 file corrisponde (vedi esempio sotto). Esempio: ambito.bin libretto.bin FORMATO stringa stringa stringa intero analisi1 mat analisi1 25 fisica1 fis fisica1 18 geometria mat geometria 29 Si suddivida opportunamente il programma in procedure / funzioni (in particolare si suggerisce una procedura che, dato in ingresso come primo parametro un array di strutture con i campi nome_esame e ambito e come secondo parametro il nome di un esame, restituisca l ambito corrispondente). Esercitazioni di Fondamenti Informatica - Modulo A 17
Si scriva un programma che legga due file binari: libretto.bin e libretto2.bin e produca in uscita un file binario in cui sono presenti tutti gli esami e il cui voto riportato è il maggiore dei due (infine lo si rilegga e stampi per verificare che sia stato scritto correttamente). Si supponga che gli esami siano presenti nello stesso ordine nei due file. Si divida opportunamente il programma in procedure / funzioni. I file libretto.bin e libretto2.bin hanno la stessa struttura composta da una stringa di al più 20 caratteri più il terminatore e un numero intero. Esercitazioni di Fondamenti Informatica - Modulo A 18
Ora avete tutti gli strumenti per poter provare a svolgere un compito di esame contenente anche file binari. Di seguito è riportato il link al testo di esame sopracitato, si provi a svolgerlo seguendo passo passo le istruzioni. Tutti i file necessari sono linkati nella pagina e scaricabili. 19 Giugno 2009 http://www.unife.it/ing/informazione/fondamenti-info-1/materiale-didattico/testi-dei-compiti/19-giugno-2009 27 Luglio 2009 http://www.unife.it/ing/informazione/fondamenti-info-1/materiale-didattico/testi-dei-compiti/27-luglio-2009 Esercitazioni di Fondamenti Informatica - Modulo A 19