la libreria standard del c - Dipartimento di Ingegneria Informatica e
Transcript
la libreria standard del c - Dipartimento di Ingegneria Informatica e
LA LIBRERIA STANDARD DEL C La libreria standard del C z z z La libreria standard del C è in realtà un insieme di librerie Per usare una libreria, non occorre inserirla esplicitamente. Ogni file sorgente che ne faccia uso deve includere lo header opportune che contiene le dichiarazioni necessari Librerie standard z z z z z input-output funzioni matematiche gestione di stringhe operazioni su caratteri Fondamenti di Informatica gestione dinamica della memoria stdio.h math.h string.h ctype.h stdlib.h 2 1 Input/Output Lettura e scrittura Caratteri e Stringhe: Terminale e file Input/output console caratteri stringhe file formattato ascii Fondamenti di Informatica binari 3 Il modello di coordinazione del C Libreria standard stdio.h z z z z z z z l’input avviene di norma dal canale standard di input (stdin) l’output avviene di norma dal canale standard di output (stdout) input e output avvengono sotto forma di una sequenza di caratteri tale sequenza è terminata dal carattere speciale EOF (end of file), che varia da una piattaforma ad un’altra di norma il canale standard stdin coincide con la tastiera di norma il canale standard stdout coincide con il video esiste inoltre un altro canale di output, riservato ai messaggi di errore: stderr Fondamenti di Informatica 4 2 Il modello di coordinazione di base Poiché sui canali di I/O fluiscono insiemi di caratteri il modello di coordinazione prevede due operazioni base z z scrivere un carattere su un canale di output z putchar(ch); leggere un carattere dal canale si input ch = getchar(); Ogni altro tipo di I/O può essere costruito a partire da queste operazioni primitive z Fondamenti di Informatica 5 Lettura e Scrittura di Caratteri int getchar(int c); z z z z z z stdio.h legge un carattere sul canale di intput stdin restituisce il carattere letto, EOF nel caso che la sequenza sia finita e in caso di errore la stdin è generalmente la tastiera ma potrebbe non esserlo la lettura è bafferizzata effettua l’echo e attende invio int putchar(int c); z z z stdio.h scrive un carattere sul canale di output (di norma il monitor) restituisce il carattere scritto, EOF in caso di errore Fondamenti di Informatica 6 3 Lettura e Scrittura di Caratteri Non sono standard e quindi non vanno usate int getch(void); void printstring(char s[]) {while (*s) putschar(*s++); putchar(‘\n’); } conio.h z ritorna immediatamente ignorando il buffer z non attende l’invio void readstring(char s[]) z non è standard {int c; z non fa l’eco sullo schermo while ((c=getchat()) != EOF && c !=‘\n’) *s++=c; *s = ‘\0’; int getche(void); } z come getch() ma fa l’eco sullo schermo. z Fondamenti di Informatica 7 Lettura e Scrittura di Stringhe z char * gets( char *s ); z z legge una stringa da tastiera e la #include <stdio.h> memorizza nella locazione indicata #include <string.h> nel parametro s. L’ingresso dei dati viene concluso con un CR che viene void main( void ) { trasformato in \0 char s[80]; char * puts(char *s); z visualizza sullo schermo il contenuto dell’argomento di tipo stringa, seguito da un CR. Questa funzione riconosce } i caratteri speciali. Il tempo necessario alla sua esecuzione e’ inferiore a quello necessario per printf() Fondamenti di Informatica do { gets(s); puts(s); } while( strlen(s) != 0 ); 8 4 I/O di valori numerici Dato un numero intero calcolare la stringa di caratteri che lo rappresenta e scrivere tale stringa sull’output standard z void printInteger(int x) { char s[20]; itoa(x,s,2); puts(s); } Dato una stringa calcolare il numero intero che rappresenta z void readInteger(int * x) { char s[20]; gets(s); *x = atoi(s); } Fondamenti di Informatica 9 Input/Output Formattato z z Le operazioni di ingresso/uscita avvengono sempre tramite un formato che resta sotto il controllo del programmatore. int printf( “stringa di controllo”, elencoArgomenti ); la stringa di controllo puo’ contenere due tipi di elementi: z caratteri che vengono scritti inalterati sullo schermo. direttive di conversione che descrivono il modo in cui gli argomenti devono essere visualizzati. Le direttive di conversione sono costituite da un segno % seguito dal carattere di conversione. printf() consente l’utilizzo di un numero molto grande di specificatori di formato. La funzione restituisce il numero di caratteri scritti o un numero negativo in caso di errore printf(“ora scrivo un intero %d, oppure una %s\n”, 10, “Stringa”); Fondamenti di Informatica 10 5 Input/Output Formattato %c %d, %i %e, %E %f %g, %G %o %s %u %x, %X %p %% singolo carattere intero con segno notazione scientifica virgola mobile il piu’ breve fra %e e %f ottale senza segno stringa di caratteri intero senza segno esadecimale puntatore Fondamenti di Informatica visualizza % a -10 1e+10 1.25 xF5 11 Modificatori di Formato z Gli specificatori di formato possono avere varianti che cambiano parzialmente il significato, specificando l’ampiezza dei campi, il numero delle cifre decimali e l’allineamento rispetto al margine sinistro. Ampiezza minima del campo: un intero posto fra il segno % e lo specificatre di formato %10d Precisione: permette di specificare il numero di cifre decimali in un numero in virgola mobile. %10.3f Allineamento: un segno - dopo il segno % fa in modo che il campo venga stampato allineato a sinistra. %-10s Il modificatore # fa stampare la variabile di tipo g, f ed e sempre con il punto decimale. Nel caso del formato x premette il prefisso 0x Fondamenti di Informatica 12 6 z z z z int scanf( char * stringaDiControllo, elencoArgomenti) permette di leggere dati dal dispositivo di ingresso Gli specificatori di formato sono analoghi a quelli della funzione printf. La presenza di caratteri diversi da quelli di formato permettono la lettura di questi caratteri senza che vengano memorizzati. Gli argomenti di scanf sono indirizzi E’ possibile specificare alcuni modificatori di formato fra cui la massima ampiezza del campo da leggere. Ad esempio scanf(“%10s %10s”, ......... ) legge al massimo dieci caratteri per ogni stringa Fondamenti di Informatica 13 I Files 7 I file z Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persistenza dei dati) è necessario poterli archiviare su memoria di massa z Un file è una 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) dello stesso tipo Un file è un’astrazione di memorizzazione di dimensione potenzialmente illimitata (ma non infinita), ad accesso sequenziale z z Fondamenti di Informatica 15 Il concetto di file z Una testina di lettura/scrittura (concettuale) indica in ogni istante il record corrente: z z z inizialmente, la testina si trova per ipotesi sulla prima posizione dopo ogni operazione di lettura / scrittura, essa si sposta sulla registrazione successiva È illecito operare oltre la fine del file Fondamenti di Informatica 16 8 File e Sistema Operativo z z A livello di sistema operativo un file è denotato univocamente dal suo nome assoluto, che comprende il percorso e il nome relativo In certi sistemi operativi il percorso può comprendere anche il nome dell’unità z z in DOS o Windows: z C:\temp\prova1.c In UNIX e Linux: z /usr/temp/prova1.c Fondamenti di Informatica 17 Apertura e chiusura di un file z Poiché un file è un’entità del sistema operativo, per agire su esso dall’interno di un programma occorre stabilire una corrispondenza fra: z z z z il nome del file come risulta al sistema operativo un nome di variabile definita nel programma Dopo l'apertura, il programma opera sul file utilizzando la variabile che lo rappresenta: il sistema operativo provvederà a effettuare l’operazione richiesta sul file associato a tale simbolo Al termine, la corrispondenza dovrà essere soppressa: operazione di chiusura del file Fondamenti di Informatica 18 9 File in C z Il tipo FILE è una struttura definita in header standard stdio.h, che l’utente non ha necessità di conoscere nei dettagli (che spesso cambia da un compilatore all’altro) z Le strutture FILE non sono mai gestite direttamente dall’utente, ma solo dalle funzioni della libreria standard stdio z L’utente definisce e usa, nei suoi programmi, solo dei puntatori a FILE Fondamenti di Informatica 19 Il modello di file in C z z z z Libreria standard stdio l’input avviene da un canale di input associato a un file aperto in lettura l’output avviene su un canale di output associato a un file aperto in scrittura Due tipi di file: file binari e file di testo z z basterebbero i file binari, ma fare tutto con essi sarebbe scomodo i file di testo, pur non indispensabili, rispondono a un’esigenza pratica molto sentita Fondamenti di Informatica 20 10 File in C: apertura Per aprire un file si usa la funzione: FILE* fopen(char fname[], char modo[]) z 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) z ATTENZIONE alle convenzioni dipendenti dal sistema operativo usato (\ oppure / nei percorsi, presenza o assenza di unità, …) Fondamenti di Informatica 21 File in C: apertura FILE* fopen(char fname[], char modo[]) z modo specifica come aprire il file: z z z z seguita opzionalmente da: z z z r apertura in lettura (read) w apertura in scrittura (write) a apertura in aggiunta (append) t apertura in modalità testo (default) b apertura in modalità binaria ed eventualmente da z + di Informatica apertura conFondamenti possibilità di modifica 22 11 File in C: apertura FILE* fopen(char fname[], char modo[]) z Il valore restituito da fopen() è un puntatore a FILE, da usare in tutte le successive operazioni sul file: z z z z NULL in caso l’apertura sia fallita controllarlo è il solo modo per sapere se il file si sia davvero aperto se non si è aperto, il programma non può proseguire Æ procedura exit() I canali predefiniti standard (stdin, stdout, stderr) sono dei file già aperti: quindi, il loro tipo è FILE* Fondamenti di Informatica 23 File in C: chiusura Per chiudere un file si usa la funzione: int fclose(FILE*) z Il valore restituito da fclose() è un intero: z z z 0 se tutto è andato bene EOF in caso di errore Prima della chiusura, tutti i buffer vengono svuotati Fondamenti di Informatica 24 12 Fine del file z La fine del file può essere rilevata: z o in base all’esito delle operazioni di lettura È l'approccio standard del C: prima si tenta una operazione di lettura, poi si guarda se è andata a buon fine, controllando il valore da essa restituito z o perché si intercetta il carattere di EOF Attenzione: lo speciale carattere EOF (End-Of-File) varia da una piattaforma all’altra Fondamenti di Informatica 25 File di testo z Un file di testo è un file che contiene sequenze di caratteri z È un caso estremamente frequente, con caratteristiche proprie: z z z esiste un concetto di riga e di fine riga (‘\n’) certi caratteri sono stampabili a video (quelli di codice >= 32), altri no la sequenza di caratteri è chiusa dal carattere speciale EOF Fondamenti di Informatica 26 13 File di testo e canali standard z I canali di I/O standard non sono altro che file di testo già aperti z z z z stdin è un file di testo aperto in lettura, di norma agganciato alla tastiera stdout è un file di testo aperto in scrittura, di norma agganciato al video stderr è un altro file di testo aperto in scrittura, di norma agganciato al video Le funzioni di I/O disponibili per i file di testo sono una generalizzazione di quelle già note per i canali di I/O standard Fondamenti di Informatica 27 File di testo e canali standard Funzione da console Funzione da file int getchar(void); int putchar(int c); char* gets(char* s); int puts(char* s); int printf(...); int scanf(...); int fgetc(FILE* f); int fputc(int c, FILE* f); char* fgets(char* s,int n, FILE* f); fputs(char* s,f, FILE* f); int fprintf(FILE* ...); z z int fscanf(FILE* f, ...); tutte le funzioni da file acquistano una “f” davanti nel nome (qualcuna però cambia leggermente nome) tutte le funzioni da file hanno un parametro in più, che è appunto il puntatore al FILE aperto Fondamenti di Informatica 28 14 I/O di caratteri: getchar() vs. fgetc() z int fgetc(FILE* f); z z int fputc(int c, FILE* f); z z Restituisce il successivo carattere contenuto nel file f, oppure EOF se incontra la fine del file Scrive il carattere c sul file f. Restituisce il carattere scritto, oppure EOF in caso di errore getchar() e putchar() sono delle scorciatoie linguistiche per fgetc() e fputc() getchar() putchar(c) ≡ ≡ fgetc(stdin) fputc(c, stdout) Fondamenti di Informatica 29 I/O di linee: fputs e fgets z char* fgets(char* s, int n, FILE* f); z z z z Legge al più i successivi n-1 caratteri dal file f e li inserisce nel vettore s, bloccandosi prima se incontra un carattere di fine linea (’\n’) La stringa s viene terminata con uno ‘\0’ Restituisce s, oppure NULL se incontra la fine del file oppure rileva un errore int fputs(char* s, FILE* f); z z Scrive la stringa s sul file f. Restituisce un valore non negativo se non ci sono errori, oppure EOF se si verifica un errore Fondamenti di Informatica 30 15 I/O formattato: fprintf e fscanf int fprintf(FILE* f, ...); int fscanf(FILE* f, ...); z fprintf e fscanf operano così come le rispettive funzioni (printf e scanf) su stdin e stdout, ma agiscono sul file f indicato come primo parametro Fondamenti di Informatica 31 Esempio 1 z Salvare su un file di testo prova.txt ciò che viene battuto sulla tastiera fp è NULL se non c’è spazio su #include <stdio.h> disco o esso è protetto da scrittura #include <stdlib.h> main(){ FILE* fp; fp = fopen("prova.txt","w"); if (fp==NULL) exit(1); /* Non si è aperto */ else { int c; while ((c=getchar())!=EOF) Per generare EOF: fputc(c, fp); - CTRL+Z (su DOS/Windows) - CTRL+D (Unix/Linux) fclose(fp); Fondamenti di Informatica 32 } } 16 Esempio 2 z Stampare a video il contenuto di un file di testo prova.txt fp può essere NULL se il file richiesto non esiste #include <stdio.h> #include <stdlib.h> main(){ FILE* fp; if ((fp = fopen("prova.txt",“r"))==NULL) exit(1); /* Errore di apertura */ else { int c; while ((c=fgetc(fp))!=EOF) putchar(c); Fondamenti di Informatica fclose(fp); } 33 Esempio 3 z È dato un file di testo people.txt le cui righe rappresentano ciascuna i dati di una persona, secondo il seguente formato: z z z z z z z cognome (al più 30 caratteri) uno o più spazi nome (al più 30 caratteri) uno o più spazi sesso (un singolo carattere, 'M' o 'F') uno o più spazi anno di nascita Fondamenti di Informatica 34 17 Esempio 3 z Si vuole scrivere un programma che z z z z legga riga per riga i dati dal file e ponga i dati in un array di persone (poi svolgeremo elaborazioni su essi) Un possibile file people.txt: Rossi Mario M 1947 Ferretti Paola F 1982 Verdi Marco M 1988 Bolognesi Annarita F 1976 ... Fondamenti di Informatica 35 Esempio 3 1) Definire il tipo persona z Occorre definire una struct adatta a ospitare i dati elencati: z z z z cognome Æ nome Æ sesso Æ anno di nascita Æ array di 30+1 caratteri array di 30+1 caratteri array di 1+1 caratteri non è la sola scelta possibile (ma è comoda…) un intero typedef struct { char cognome[31], nome[31], sesso[2]; int anno; } persona; Fondamenti di Informatica 36 18 Esempio 3 Poi, nel main: 2) definire un array di persona 3) aprire il file in lettura int main() { persona v[DIM]; FILE* f = fopen("people.txt", "r"); apertura in lettura if (f==NULL) { ... } ... Fondamenti di Informatica } 37 Esempio 3 Poi, nel main: 2) definire un array di persona 3) aprire il file in lettura int main() { persona v[DIM]; perror(msg) stampa FILE* f = fopen("people.txt", "r"); un messaggio d’errore sul canale stderr if (f==NULL) { perror(“Il file non esiste!”); exit(1); exit(n) fa terminare il programma, } restituendo al s.o. il valore n come codice di errore ... Fondamenti di Informatica } 38 19 Esempio 3 Poi, nel main: 4) leggere una riga per volta, e porre i dati di quella persona in una cella dell'array z Come organizzare la lettura? z z Dobbiamo leggere delle stringhe separate una dall'altra da spazi Sappiamo che ogni singola stringa (cognome, nome, sesso) non contiene spazi Scelta più pratica: Fondamenti di Informatica fscanf 39 Esempio 3 z Cosa far leggere a fscanf? z z z Tre stringhe separate una dall'altra da spazi z si ripete tre volte il formato %s Un intero Æ si usa il formato %d Il fine riga Æ occorre specificare in fondo \n fscanf(f, "%s%s%s%d\n", …) z Fino a quando si deve leggere? z z Quando il file termina, fscanf restituisce EOF z basta controllare il valore restituito Si continua fintanto che è diverso da EOF while(fscanf(…)!=EOF) Fondamenti di Informatica ... 40 20 Esempio 3 z Dove mettere quello che si legge? z z z Abbiamo definito un array di persona, v Struttura fatta di cognome, nome, sesso, anno Æ ciò che si estrae da una riga va nell'ordine in v[k].cognome, v[k].nome, v[k].sesso, v[k].anno E dopo aver letto una riga? z z La testina di lettura sul file è già andata a capo, perché il formato di lettura prevedeva esplicitamente di consumare il fine linea (\n) L'indice k invece indica ancora la cella appena occupata Æ occorre incrementarlo, affinché indichi la prossima cella Fondamenti di Informatica 41 libera Esempio 3 Poi, nel main: 4) leggere una riga per volta, e porre i dati di quella persona in una cella dell'array int main() { int k=0; /* indice per array */ ... while(fscanf(f,"%s%s%s%d\n", v[k].cognome, v[k].nome, v[k].sesso, &v[k].anno ) != EOF){ k++; /* devo incrementare k */ Ricorda: lintero richiede l’estrazione } esplicita dell’indirizzo della variabile Fondamenti di Informatica } 42 21 Esempio 3 Poi, nel main: 4) leggere una riga per volta, e porre i dati di quella persona in una cella dell'array Ricordare: z fscanf elimina automaticamente gli spazi che separano una stringa dall'altra, quindi… z non si devono inserire spazi nella stringa di formato z fscanf considera finita una stringa al primo spazio che trova, quindi… z non si può usare questo metodo per leggere stringhe contenenti spazi Fondamenti di Informatica 43 Esempio 3: programma completo #define DIM 30 #include <stdio.h> #include <stdlib.h> typedef struct { char cognome[31], nome[31], sesso[2]; int anno; } persona; int main() { persona v[DIM]; int k=0; FILE* f; if ((f=fopen("people.txt", "r"))==NULL) { perror("Il file non esiste!"); exit(1); } while(fscanf(f,"%s%s%s%d\n", v[k].cognome, v[k].nome, v[k].sesso, &v[k].anno ) != EOF) k++; Fondamenti di Informatica 44 } 22 Esempio 3: variante z E se usassimo un singolo carattere per rappresentare il sesso? z Non più: typedef struct { char cognome[31], nome[31], sesso[2]; int anno; } persona; z Ma: typedef struct { char cognome[31], nome[31], sesso; int anno; } persona; Fondamenti di Informatica 45 Esempio 3: variante z Cosa cambierebbe? z fscanf elimina automaticamente gli spazi prima di leggere una stringa o un numero (intero o reale)... ma non prima di leggere un singolo carattere, perché se lo facesse non riuscirebbe a leggere il carattere spazio Ma noi non sappiamo quanti spazi ci sono fra nome e sesso Quindi, non possiamo sapere a priori dov'è il carattere che ci interessa z z Fondamenti di Informatica 46 23 Esempio 3: variante z Infatti, il nostro file potrebbe essere fatto così: Rossi Mario M 1947 Ferretti Paola F 1982 Verdi Marco M 1988 Bolognesi Annarita F 1976 ... z z Qui uno spazio prima di M Qui due spazi prima di F Qui tre spazi prima di M Qui uno spazio prima di F prima, dicendo a fscanf si leggere una stringa, gli spazi (uno, due, tre..) erano eliminati comunque adesso, dicendo a fscanf si leggere un carattere singolo, dobbiamo decidere noi cosa fare Fondamenti di Informatica 47 Esempio 3: variante Due possibilità: z scelta 1: introdurre comunque una stringa di due caratteri e usarla per far leggere il carattere relativo al sesso a fscanf Poi, copiare il primo carattere al suo posto z scelta 2: costruirsi un ciclo che salti tutti gli spazi fino al primo carattere non-spazio, poi recuperare quest'ultimo… z non consente più di usare fscanf per gestire tutta la fase di lettura Fondamenti di Informatica 48 24 Variante 1 ... typedef struct { char cognome[31], nome[31], sesso; Stringa ausiliaria int anno; } persona; int main() { persona v[DIM]; int k=0; FILE* f; char s[2]; if ((f=fopen("people.txt", "r"))==NULL) { perror("Il file non esiste!"); exit(1); } while(fscanf(f,"%s%s%s%d\n", v[k].cognome, v[k].nome, s, &v[k].anno ) != EOF){ v[k].sesso = s[0]; Copiatura caratteri k++; Fondamenti di Informatica } 49 Variante 2 ... typedef struct { char cognome[31], nome[31], sesso; int anno; } persona; int main() { persona v[DIM]; int k=0; FILE* f; char ch; if ((f=fopen("people.txt", "r"))==NULL) { perror("Il file non esiste!"); exit(1); } while(fscanf(f,"%s%s", v[k].cognome,v[k].nome) != EOF){ while((ch=fgetc(f))==' '); v[k].sesso = ch; fscanf(f,"%d\n",&v[k].anno); k++; Fondamenti di Informatica } } 50 25 Variante 2: alternativa ... typedef struct { char cognome[31], nome[31], sesso; int anno; } persona; int main() { persona v[DIM]; int k=0; FILE* f; char ch; if ((f=fopen("people.txt", "r"))==NULL) { perror("Il file non esiste!"); exit(1); } while(fscanf(f,"%s%s", v[k].cognome,v[k].nome) != EOF){ do fscanf(f,”%c”, &ch); while (ch==‘ ‘); v[k].sesso = ch; fscanf(f,"%d\n",&v[k].anno); k++; Fondamenti di Informatica } } 51 Esempio 4 z È dato un file di testo elenco.txt le cui righe rappresentano ciascuna i dati di una persona, secondo il seguente formato: z z z z z z cognome (esattamente 10 caratteri) nome (esattamente 10 caratteri) sesso (esattamente un carattere) anno di nascita I primi due possono contenere spazi al loro interno NB: non sono previsti spazi espliciti di separazione Fondamenti di Informatica 52 26 Esempio 4 Cosa cambia rispetto a prima? sappiamo esattamente dove iniziano e dove finiscono i singoli campi z non possiamo sfruttare gli spazi per separare cognome e nome Un possibile file elenco.txt: I vari campi possono essere z Rossi Ferretti De Paoli Verdi Bolognesi ... Mario M1947 Paola F1982 Gian MarcoM1980 Marco M1988 Annarita F1976 attaccati: tanto, sappiamo a priori dove inizia l’uno e finisce l’altro Fondamenti di Informatica 53 Esempio 4 z Come fare le letture? z z z z non possiamo usare fscanf(f,"%s",…) si fermerebbe al primo spazio potrebbe leggere più caratteri del necessario (si pensi a Gian MarcoM1988) però possiamo usare fscanf nell'altra modalità, specificando quanti caratteri leggere. Ad esempio, per leggerne dieci: fscanf(f,"%10c",…) z Così legge esattamente 10 caratteri, spazi inclusi Fondamenti di Informatica 54 27 Esempio 4 z ATTENZIONE: fscanf(f,"%10c",…) z In questo modo viene riempito un array di caratteri, senza inserire alcun terminatore di stringa. Occorre aggiungerlo a parte v[k].nome[10] = ‘\0’; Fondamenti di Informatica 55 Esempio 4: programma completo ... typedef struct { char cognome[11], nome[11], sesso; int anno; } persona; int main() { persona v[DIM]; int k=0; FILE* f; if ((f=fopen("elenco.txt", "r"))==NULL) { perror("Il file non esiste!"); exit(1); } while(fscanf(f,"%10c%10c%c%d\n", v[k].cognome, v[k].nome, &v[k].sesso, &v[k].anno ) != EOF){ v[k].cognome[10] = ‘\0’; v[k].nome[10] ='\0'; k++; Fondamenti di Informatica } } 56 28 File binari z Un file binario è una pura sequenza di byte, senza alcuna strutturazione particolare z È un'astrazione di memorizzazione assolutamente generale, usabile per memorizzare su file informazioni di qualsiasi natura z z z z z "fotografie" della memoria rappresentazioni interne binarie di numeri immagini, audio, musica, …. ...volendo, anche caratteri I file di testo non sono indispensabili: sono semplicemente comodi Fondamenti di Informatica 57 File binari z Un file binario è una sequenza di byte z Può essere usato per archiviare su memoria di massa qualunque tipo di informazione Input e output avvengono sotto forma di una sequenza di byte La fine del file è rilevata in base all’esito delle operazioni di lettura z z z z non c'è EOF, perché un file binario non è una sequenza di caratteri qualsiasi byte si scegliesse come marcatore, potrebbe sempre capitare nella sequenzaFondamenti di Informatica 58 29 File binari z Poiché un file binario è una sequenza di byte, sono fornite due funzioni per leggere e scrivere sequenze di byte z z z fread() legge una sequenza di byte fwrite() scrive una sequenza di byte Essendo pure sequenze di byte, esse non sono interpretate. Quindi, possono rappresentare qualunque informazione (testi, numeri, immagini…) Fondamenti di Informatica 59 Output binario: fwrite() Sintassi: int fwrite(addr, int dim, int n, FILE *f); z scrive sul file n elementi, ognuno grande dim byte (complessivamente, scrive quindi n×dim byte) z gli elementi da scrivere vengono prelevati dalla memoria a partire dall’indirizzo addr z restituisce il numero di elementi (non di byte) effettivamente scritti, che possono essere meno di n Fondamenti di Informatica 60 30 Input binario: fread() Sintassi: int fread(addr, int dim, int n, FILE *f); z legge dal file n elementi, ognuno grande dim byte (complessivamente, tenta di leggere quindi n×dim byte) z gli elementi da leggere vengono scritti in memoria a partire dall’indirizzo addr z restituisce il numero di elementi (non di byte) effettivamente letti, che possono essere meno di n se il file finisce prima. z Controllare il valore restituito è il solo modo per sapere cosa è stato letto, e in particolare per scoprire se il file è finito Fondamenti di Informatica 61 Esempio 1 z Salvare su un file binario numeri.dat il contenuto di un array di dieci 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) exit(1); /* Errore di apertura */ fwrite(vet, sizeof(int), 10, fp); fclose(fp); L’operatore sizeof() è essenziale per la portabilità: } Fondamenti di Informatica 62 la dimensione di int non è fissa 31 Esempio 2 z Leggere da un file binario numeri.dat una sequenza di interi, scrivendoli in un array fread tenta di leggere 40 interi, ne legge meno se il file finisce prima #include <stdio.h> #include <stdlib.h> int main(){ FILE *fp; int vet[40], i, n; if ((fp = fopen("numeri.dat","rb"))==NULL) exit(1); /* Errore di apertura */ n = fread(vet,sizeof(int),40,fp); for (i=0; i<n; i++) n contiene il numero di printf("%d ",vet[i]); Fondamenti di Informatica interi effettivamente63letti fclose(fp); } Esempio 3 z Scrivere su un file di caratteri testo.txt una sequenza di caratteri Dopo averlo creato, provare ad aprire #include <stdio.h> questo file con un editor di testi #include <stdlib.h> qualunque (… e il terminatore?) main(){ FILE *fp; int n; char msg[] = "Ah, l'esame\nsi avvicina!"; if ((fp = fopen("testo.txt","wb"))==NULL) exit(1); /* Errore di apertura */ fwrite(msg, strlen(msg)+1, 1, fp); fclose(fp); } Un carattere in C ha sempre size 1 Scelta: salvare anche il terminatore Fondamenti di Informatica 64 32 Esempio 4 z Leggere da un file di caratteri testo.txt una sequenza di caratteri, ponendoli in una stringa #include <stdio.h> #include <stdlib.h> main(){ FILE *fp; char msg[80], n; if ((fp = fopen("testo.txt","rb"))==NULL) exit(1); /* Errore di apertura */ n = fread(msg, 1, 80, fp); puts(msg); N contiene il numero di char effettivamente letti (che non ci interessa, perché tanto c’è il terminatore) fclose(fp); Fondamenti di Informatica 65 } Esempio 5: output di numeri z L’uso di file binari consente di rendere evidente la differenza fra la rappresentazione interna di un numero e la sua rappresentazione esterna come stringa di caratteri in una certa base z z Supponiamo che sia int x = 31466; Che differenza c’è fra: fprintf(file,"%d", x); Fondamenti di Informatica 66 fwrite(&x, sizeof(int), 1, file); 33 Esempio 5: output di numeri z z z Se x è un intero che vale 31466, internamente la sua rappresentazione è (su 16 bit): 01111010 11101010 fwrite() emette direttamente tale sequenza, scrivendo quindi i due byte sopra indicati fprintf() invece emette la sequenza di caratteri ASCII corrispondenti alla rappresentazione esterna del numero 31466, ossia i cinque byte 00110011 00110001 00110100 00110110 00110110 z Se per sbaglio si emettessero su un file di testo (o su video) direttamente i due byte: 01111010 11101010 si otterrebbero i caratteri corrispondenti al codice ASCII di quei byte: êz Fondamenti di Informatica 67 Esempio 6: input di numeri z Analogamente, che differenza c’è fra fscanf(file, "%d", &x); e fread(&x, sizeof(int), 1, file); z nell’ipotesi che il file (di testo) contenga la sequenza di caratteri “23” ? Fondamenti di Informatica 68 34 Esempio 6: input di numeri z z fscanf() preleva la stringa di caratteri ASCII 00110010 00110011 che costituisce la rappresentazione esterna del numero, e la converte nella corrispondente rappresentazione interna, ottenendo i due byte: 00000000 00010111 z che rappresentano in binario il valore ventitre Fondamenti di Informatica 69 Esempio 6: input di numeri z fread() invece preleverebbe i due byte 00110010 00110011 Carattere ‘2’ z z Carattere ‘3’ credendoli già la rappresentazione interna di un numero, senza fare alcuna conversione Tale modo di agire porterebbe a inserire nella variabile x esattamente la sequenza di byte sopra indicata, che verrebbe quindi interpretata come il numero tredicimilacentosei Fondamenti di Informatica 70 35 Esempio completo file binario z È dato un file di binario people.dat i cui record rappresentano ciascuno i dati di una persona, secondo il seguente formato: z z z z z cognome (al più 30 caratteri) nome (al più 30 caratteri) sesso (un singolo carattere, 'M' o 'F') anno di nascita Si noti che la creazione del file binario deve essere fatta da programma, mentre per i file di testo può essere fatta con un text editor Fondamenti di Informatica 71 Esempio completo file binario z È necessario scrivere un programma che lo crei strutturandolo in modo che ogni record contenga una typedef struct { char cognome[31], nome[31], sesso[2]; int anno; } persona; z I dati di ogni persona da inserire nel file vengono richiesti all’utente tramite la funzione leggiel() che non ha parametri e restituisce come valore di ritorno la struct persona letta. Quindi il prototipo è: persona leggiel(); Fondamenti di Informatica 72 36 Creazione file binario persona leggiel(){ persona e; printf(”Cognome ? "); scanf("%s", e.cognome); printf("\n Nome ? "); scanf("%s",e.nome); printf("\nSesso ? "); scanf("%s",e.sesso); printf("\nAnno nascita ? "); scanf("%d", &e.anno); return e; } Fondamenti di Informatica 73 Creazione file binario ... typedef struct { char cognome[31], nome[31], sesso[2]; int anno; } persona; main(){ FILE *f; persona e; int fine=0; if ((f=fopen(“people.dat”, "wb"))==NULL) exit(1); while (!fine) { e=leggiel(); fwrite(&e,sizeof(persona),1,f); printf("\nFine (SI=1, NO=0) ? "); scanf("%d", &fine); } fclose(f); Fondamenti di Informatica } 74 37 Creazione file binario z z L’esecuzione del programma precedente crea il file binario contenente i dati immessi dall’utente. Solo a questo punto il file può essere utilizzato Il file people.dat non è visualizzabile tramite un text editor: questo sarebbe il risultato rossi > ÿÿ @ T —8 â3 mario ôÜ _ ôÜ Aw O F _ DÝ M nuinH2ô1 ô1 ô1 Fondamenti di Informatica 75 Esempio completo file binario z Ora si vuole scrivere un programma che z z z legga record per record i dati dal file ponga i dati in un array di persone (poi svolgeremo elaborazioni su essi) Fondamenti di Informatica 76 38 Esempio completo file binario 1) Definire il tipo persona z Occorre definire una struct adatta a ospitare i dati elencati: z z z z cognome Æ nome Æ sesso Æ anno di nascita Æ array di 30+1 caratteri array di 30+1 caratteri array di 1+1 caratteri un intero non è la sola scelta typedef struct { possibile (ma è comoda…) char cognome[31], nome[31], sesso[2]; int anno; } persona; Fondamenti di Informatica 77 Esempio completo file binario Poi, nel main: 2) definire un array di persona 3) aprire il file in lettura (modalità binaria) int main() { persona v[DIM]; FILE* f = fopen("people.dat", "rb"); if (f==NULL) { perror(“Il file non esiste!”); exit(1); } ... exit(n) fa terminare il programma, Fondamenti di Informatica restituendo al s.o. il valore n come } perror(msg) stampa un messaggio d’errore sul canale stderr 78 codice di errore 39 Esempio completo file binario Poi, nel main: 4) leggere i record dal file (non c’è più il concetto di riga), e porre i dati di quella persona in una cella dell'array z Come organizzare la lettura? int fread(addr, int dim, int n, FILE *f); z z legge dal file n elementi, ognuno grande dim byte (complessivamente, tenta di leggere quindi n×dim byte) gli elementi da leggere vengono scritti in memoria a partire dall’indirizzo addr Fondamenti di Informatica 79 Esempio completo file binario z Cosa far leggere a fread? z L’intero vettore di strutture: unica lettura per DIM record fread(v,sizeof(persona),DIM,f) z Un record alla volta all’interno di un ciclo i=0; while(!feof(f)){ fread(&v[i],sizeof(persona),1,f); i++; } Fondamenti di Informatica 80 40 Esempio completo file binario: variante 1 #define DIM 30 #include <stdio.h> #include <stdlib.h> typedef struct { char cognome[31], nome[31], sesso[2]; int anno; } persona; main() { persona v[DIM]; int i=0; FILE* f; if ((f=fopen("people.dat", "rb"))==NULL) { printf("Il file non esiste!"); exit(1); } while(fread(&v[i],sizeof(persona),1,f)>0){ i++; Fondamenti di Informatica } } 81 Esempio completo file binario: variante 2 #define DIM 30 #include <stdio.h> #include <stdlib.h> typedef struct { char cognome[31], nome[31], sesso[2]; int anno; } persona; main() { persona v[DIM]; int i=0; FILE* f; if ((f=fopen("people.dat", "rb"))==NULL) { printf("Il file non esiste!"); exit(1); } fread(v,sizeof(persona),DIM,f); Fondamenti di Informatica } 82 41 File Binari: feof() int feof(FILE *fp) z controlla se nella precedente operazione di lettura/scrittura sul file cui fa riferimento fp è stata raggiunta la fine del file. z Restituisce: z z 0 se la condizione di fine file non è stata raggiunta un valore diverso da 0 in caso contrario Fondamenti di Informatica 83 File: accesso diretto z In alcuni casi può risultare utile accedere direttamente ad un particolare byte all’interno di un file. Vengono usate le seguenti funzioni di libreria: fseek z ftell z rewind z Fondamenti di Informatica 84 42 File: accesso diretto int fseek(FILE *fp, long offset, int origin) z Consente di spostare arbitrariamente la testina di lettura/scrittura per un file z z fp : file su cui si sta operando offset : dà la posizione, rispetto a origin, a cui portarsi sul file (è espresso in byte è può assumere valori positivi o negativi) N.B.: per un file di testo deve valere 0 o un valore restituito da ftell z origin : dà la posizione rispetto a cui misurare l’offset Fondamenti di Informatica 85 File: accesso diretto origin può assumere i seguenti tre valori: z z z SEEK_SET: indica l’inizio del file SEEK_CUR: indica la posizione corrente SEEK_END: indica la fine del file Fondamenti di Informatica 86 43 File: accesso diretto long ftell(FILE *fp) z Restituisce la posizione corrente nel file specificato Fondamenti di Informatica 87 File: accesso diretto void rewind(FILE *fp) z Posiziona la testina di lettura/scrittura all’inizio del file z rewind(f) equivale a fseek(f, 0, SEEK_SET) Fondamenti di Informatica 88 44