Gestione dei File in C
Maurizio Palesi DIIT—Università di Catania Viale Andrea Doria 6, 95125 Catania
[email protected]
http://www.diit.unict.it/users/mpalesi
Sommario
In questo documento saranno introdotte le funzioni per la gestione dei file in C. L’importanza dei file risiede nel fatto che per mezzo di esso è possibile conservare grosse quantità di dati che debbano essere utilizzate in tempi diversi o che debbano essere aggiornate.
Indice
1 Generalità 1
2 Flusso 1
3 File 2
3.1 fopen() . . . 2
3.2 fclose() . . . 3
3.3 feof() . . . 3
3.4 fscanf()efprintf() . . . 3
4 Accesso a Blocchi 8 4.1 fread()efwrite(). . . 8
4.2 fseek() . . . 13
4.3 ftell() . . . 13
1 Generalità
Il linguaggio C non contiene alcuna istruzione di Input/Output. Tali operazioni vengono svolte mediante chiamate a funzioni definite nella libreria standard contenute nel file stdio.h. Tali funzioni rendono possibile la lettura/scrittura in modo indipendente dalle caratteristiche proprie dei dispositivi di Input/Output. Le stesse funzioni possono essere utilizzate, ad esempio, sia per leggere un valore dalla tastiera sia per leggere un valore da un dispositivo di memoria di massa. Lo stesso vale per le funzioni di scrittura: le stesse operazioni possono essere utilizzate sia per la visualizzazione sullo schermo sia per scrivere un valore su un disco o una stampante. Ciò è possibile poichè il sistema di I/O C è caratterizzato da un’interfaccia indipendente dal dispositivo effettivo che si interpone tra il programmatore e il dispositivo. Tale interfaccia è chiamata flusso, mentre il dispositivo effettivo è chiamato file.
2 Flusso
Il sistema di I/O C associa ad ogni dispositivo fisico un dispositivo logico chiamato flusso. Poichè tutti i flussi si comportano alla stessa maniera, possono essere utilizzate le stesse funzioni per la loro gestione.
Esistono due tipi di flussi: flussi binari e di testo. Un flusso binario è formato da una sequenza di byte con una corrispondenza uno ad uno con i byte presenti sul dispositivo fisico. Un flusso di testo è una sequenza di
Modalita Descrizione
r Apre un file di testo in lettura w Crea un file di testo in scrittura
a Apre un file di testo in modalità append rb Apre un file binario in lettura
wb Crea un file binario in scrittura
ab Apre un file binario in modalità append r+ Apre un file di testo in lettura/scrittura w+ Crea un file di testo in lettura/scrittura
a+ Crea o apre un file di testo in modalità append per lettura/scritura r+b Apre un file binario in lettura/scrittura
w+b Crea un file binario in lettura/scrittura
a+b Crea o apre un file binario in modalità append per lettura/scrittura Tabella 1: Modalità di apertura di un file.
caratteri generalmente suddivisa in linee terminate da un carattere di newline. In un flusso di testo alcuni ca- ratteri potrebbero essere tradotti. Ad esempio il newline può essere convertito sulla stampante, che costituisce il dispositivo fisico, in un ritorno a capo e salto a un nuova linea della testina.
3 File
Un File è un qualsiasi dispositivo, da un disco a un terminale a una stampante. Per associare un flusso a un file è necessario un’operazione di apertura. Una volta aperto un file sarò possibile scambiare informazioni tra il file e il programma.
Per eliminare l’associazione tra flusso e file è necessaria un’operazione di chiusura. Nel caso un file aperto in scrittura, l’eventuale contenuto del flusso viene scritto sul dispositivo fisico.
Ogni flusso ha associato una struttura chiamata FILE contenente i seguenti campi:
Modalità di utilizzo del file (lettura, scrittura o lettura e scrittura);
Posizione corrente su file (indicante il prossimo byte da leggere o scrivere su file);
Un indicatore di errore di lettura/scrittura;
Un indicatore di end-of-file, indicante il raggiungimento della fine del file.
Ogni operazione di apertura a file restituisce un puntatore a una variabile di tipo FILE. Per potere leggere e scrivere i file è necessario usare delle variabili di tipo puntatore a file, dichiarate nel seguente modo:
FILE p f ;
3.1 fopen()
L’apertura di un file viene realizzata mediante la funzionefopen()avente il seguente prototipo:
FILE f o p e n ( c h ar n o m e f i l e , c h ar à m o d a l i t ) ;
dovenomefileè una stringa di caratteri indicante il nome del file da aprire emodalitàindica il modo in cui il file deve essere aperto (Si veda la Tabella 1). Se si verifica un errore in apertura del file, lafopen() restituisce un puntatore nullo.
Volendo, ad esempio, aprire in scrittura il filetestscriveremo:
FILE p f ;
p f = f o p e n ( " t e s t " , "w" ) ; i f ( p f = = NULL)
{
p r i n t f ( " I m p o s s i b i l e a p r i r e i l f i l e " ) ; e x i t ( 1 ) ;
}
In generale, prima di accedere ad un file occorre assicurarsi che la chiamata afopen()sia stata eseguita con successo.
3.2 fclose()
La chiusura di un file viene realizzata mediante la funzione fclose avente il seguente prototipo:
i n t f c l o s e ( FILE p f ) ;
dovepfè il puntatore restituito dallafopen().
3.3 feof()
La funzionefeof()restituisce un valore logico vero nel caso in cui è raggiunta la fine del file e zero in tutti gli altri casi. Il prototipo dellafeofè il seguente:
int feof(FILE *pf);
3.4 fscanf() e fprintf()
Le funzionifscanf()efprintf()vengono utilizzate per la lettura e la scrittura su file. Il loro compor- tamento è lo stesso delle funzioniscanf()eprintf(). Il loro prototipo è il seguente:
i n t f s c a n f ( FILE p f , c o n s t c h ar s t r i n g a _ d i _ c o n t r o l l o , . . . ) ; i n t f p r i n t f ( FILE p f , c o n s t c h ar s t r i n g a _ d i _ c o n t r o l l o , . . . ) ;
Esercizio 3.1 ()
Lettura da file e visualizzazione sullo schermo.
Risoluzione
# i n c l u d e < s t d i o . h>
v o i d main ( v o i d ) {
FILE p f ; i n t a ;
p f = f o p e n ( " n u m e r i . t x t " , " r " ) ; i f ( p f )
{
w h i l e ( ! f e o f ( p f ) ) {
f s c a n f ( p f , "%d \ t " , & a ) ; p r i n f ( "%d \ n " , a ) ;
}
f c l o s e ( p f ) ; }
e l s e
p r i n t f ( " e r r o r e d u r a n t e l a l ’ a p e r t u r a d e l f i l e . " ) ; }
Esercizio 3.2 ()
Lettura da tastiera e scrittura su file.
Risoluzione
# i n c l u d e < s t d i o . h>
v o i d main ( v o i d ) {
i n t num , a ; FILE p f ;
p r i n t f ( " Q u a n t i n u m e r i v u o i i n s e r i r e ? " ) ; s c a n f ( "%d " , & num ) ;
p f = f o p e n ( " d a t i . t x t " , "w" ) ; i f ( p f )
{
f o r ( ; num ; num ) {
p r i n t f ( " I n s e r i s c i un nuovo numero : " ) ; s c a n f ( "%d " , & a ) ;
f p r i n t f ( p f , "%d \ t " , a ) ; }
f c l o s e ( p f ) ; }
e l s e
p r i n t f ( " E r r o r e d u r a n t e l ’ a p e r t u r a d e l f i l e . " ) ; }
Esercizio 3.3 ()
Lettura da file e visualizzazione sullo Standard Output di stringhe.
Risoluzione
# i n c l u d e < s t d i o . h>
v o i d main ( v o i d ) {
FILE p f ;
c h ar nome [ 3 0 ] , cognome [ 2 0 ] ; i n t e s a m i ;
p f = f o p e n ( " d a t i . t x t " , " r " ) ; i f ( p f )
{
w h i l e ( ! f e o f ( p f ) ) {
f s c a n f ( p f , "% s \ t " , nome ) ; f s c a n f ( p f , "% s \ t " , cognome ) ; f s c a n f ( p f , "%d \ t " , & e s a m i ) ;
p r i n t f ( "Nome : % s \ tCognome : % s \ tNum . e s a m i : % d \ n " , nome , cognome , e s a m i ) ;
}
f c l o s e ( p f ) ; }
e l s e
p r i n t f ( " E r r o r e d u r a n t e l ’ a p e r t u r a d e l f i l e . " ) ; }
Esercizio 3.4 ()
Massimo tra numeri letti da file (Versione senza funzioni).
Risoluzione
# i n c l u d e < s t d i o . h>
v o i d main ( v o i d ) {
FILE p f ; i n t max , a ;
p f = f o p e n ( " n u m e r i . t x t " , " r " ) ; i f ( p f )
{
i f ( ! f e o f ( p f ) )
f s c a n f ( p f , "%d \ t " , & max ) ; w h i l e ( ! f e o f ( p f ) )
{
f s c a n f ( p f , "%d \ t " , & a ) ; i f ( max < a )
max= a ; }
p r i n t f ( "%d \ n " , a ) ; f c l o s e ( p f ) ;
} e l s e
p r i n t f ( " E r r o r e d u r a n t e l ’ a p e r t u r a " ) ;
}
Esercizio 3.5 ()
Massimo tra numeri letti da file (Versione con funzioni).
Risoluzione
# i n c l u d e < s t d i o . h>
i n t MaxDaFile ( c h ar n o m e f i l e , i n t max ) ; v o i d main ( v o i d )
{
i n t massimo ;
i f ( MaxDaFile ( " n u m e r i . t x t " , & massimo ) ) p r i n t f ( " I l massimoè : % d " , massimo ) ; e l s e
p r i n t f ( " E r r o r e s u f i l e " ) ; }
i n t MaxDaFile ( c h ar n o m e f i l e , i n t max ) {
FILE p f ; i n t a ;
p f = f o p e n ( n o m e f i l e , " r " ) ; i f ( p f )
{
i f ( ! f e o f ( p f ) )
f s c a n f ( p f , "%d \ t " , max ) ; w h i l e ( ! f e o f ( p f ) )
{
f s c a n f ( p f , "%d \ t " , & a ) ; i f ( max < a )
max = a ; }
f c l o s e ( p f ) ; r e t u r n 1 ; }
e l s e
r e t u r n 0 ; }
Esercizio 3.6 ()
Lettura da tastiera di un vettore di struct e scrittura su file.
Risoluzione
# i n c l u d e < s t d i o . h>
s t r u c t D a t o { l o n g m a t r i c o l a ; i n t n m a t e r i e ; } ;
v o i d L e g g i ( s t r u c t D a t o d ) ;
v o i d S c r i v i S u F i l e ( c h ar n o m e f i l e , s t r u c t D a t o v e c t ) ; v o i d main ( v o i d )
{
s t r u c t D a t o v e t t o r e [ 1 0 ] ; i n t i ;
f o r ( i = 0 ; i < 1 0 ; i ++) L e g g i (& v e t t o r e [ i ] ) ;
S c r i v i S u F i l e ( " v o t i . t x t " , v e t t o r e ) ; }
v o i d L e g g i ( s t r u c t D a t o d ) {
p r i n t f ( " M a t r i c o l a : " ) ; s c a n f ( "% l d " , & d > m a t r i c o l a ) ; p r i n t f ( " Numero d i m a t e r i e : " ) ; s c a n f ( "%d " , & d > n m a t e r i e ) ; }
v o i d S c r i v i S u F i l e ( c h ar n o m e f i l e , s t r u c t D a t o v ) {
FILE p f ; i n t i ;
p f = f o p e n ( n o m e f i l e , "w" ) ; i f ( p f )
{
f o r ( i = 0 ; i < 1 0 ; i ++)
f p r i n t f ( p f , "% l d \ t%d \ n " , v [ i ] . m a t r i c o l a , v [ i ] . n m a t e r i e ) ; f c l o s e ( p f ) ;
} e l s e
p r i n t f ( " E r r o r e " ) ; }
4 Accesso a Blocchi
4.1 fread() e fwrite()
E’ possibile accedere in lettura o scrittura ai dati di un file operando du un intero blocco di dati testuali o binari di qualsiasi dimensione. Le funzioni utilizzati sonofread()efwrite()i cui prototipi sono:
i n t f r e a d ( v o i d p u n t , i n t d i m e n s i o n e _ e l e m e n t o , i n t n u m e r o _ e l e m e n t i , FILE p f ) ; i n t f w r i t e ( v o i d p u n t , i n t d i m e n s i o n e _ e l e m e n t o , i n t n u m e r o _ e l e m e n t i , FILE p f ) ;
La funzionefread()legge un blocco dinumero_elementi, ciascuno didimensione_elemento byte, dal file cui fa riferimento il puntatorepfe li copia in memoria a partire dall’indirizzo indicato dal puntatorepunt. La funzione restituisce il numero di elementi effettivamente letti; tale numero è inferiore al numero di elementi richiesti o perchè c’è stato un errore in lettura o perchè si è arrivati alla fine del file.
La funzionefwrite()scrive un blocco dinumero_elementi, ciascuno didimensione_elemento byte, sul file cui fa riferimento il puntatorepfprelevandoli dalla memoria a partire dall’indirizzo indicato dal puntatorepunt. La funzione restituisce il numero di elementi effettivamente scritti; tale numero è inferiore al numero di elementi richiesti perchè c’è stato un errore in scrittura.
Esercizio 4.1 ()
Scrittura e successiva lettura di numeri interi su file.
Risoluzione
# i n c l u d e < s t d i o . h>
main ( ) {
FILE p f ; i n t i , num ;
p f = f o p e n ( " n u m e r i . d a t " , "w" ) ; i f ( p f )
{
f o r ( i = 0 ; i < 4 ; i ++) {
p r i n t f ( " I n s e r i s c i un nuovo numero : " ) ; s c a n f ( "%d " , & num ) ;
f w r i t e (&num , s i z e o f ( i n t ) , 1 , p f ) ; }
f c l o s e ( p f ) ; }
e l s e e x i t ( 1 ) ;
p f = f o p e n ( " n u m e r i . d a t " , " r " ) ; i f ( p f )
{
f o r ( i = 0 ; i < 4 ; i ++) {
f r e a d (&num , s i z e o f ( i n t ) , 1 , p f ) ; p r i n t f ( "%d \ n " , num ) ;
}
f c l o s e ( p f ) ; }
e l s e e x i t ( 1 ) ; }
Esercizio 4.2 ()
Scrittura e successiva lettura di strutture su file (versione 1).
Risoluzione
# i n c l u d e < s t d i o . h>
s t r u c t E l e m e n t o { l o n g m a t r i c o l a ; i n t m a t e r i e ; } ;
main ( ) {
FILE p f ; i n t i , num ;
s t r u c t E l e m e n t o e l ;
p f = f o p e n ( " n u m e r i . d a t " , "w" ) ; i f ( p f )
{
f o r ( i = 0 ; i < 4 ; i ++) {
p r i n t f ( " I n s e r i s c i l a m a t r i c o l a : " ) ; s c a n f ( "% l d " , & ( e l . m a t r i c o l a ) ) ;
p r i n t f ( " I n s e r i s c i i l numero d i e s a m i s u p e r a t i : " ) ; s c a n f ( "%d " , & e l . m a t e r i e ) ;
f w r i t e (& e l , s i z e o f ( s t r u c t E l e m e n t o ) , 1 , p f ) ; }
f c l o s e ( p f ) ; }
e l s e e x i t ( 1 ) ;
p f = f o p e n ( " n u m e r i . d a t " , " r " ) ; i f ( p f )
{
f o r ( i = 0 ; i < 4 ; i ++) {
f r e a d (& e l , s i z e o f ( s t r u c t E l e m e n t o ) , 1 , p f ) ;
p r i n t f ( " M a t r i c o l a : % l d \ t M a t e r i e : % d \ n " , e l . m a t r i c o l a , e l . m a t e r i e ) ; }
f c l o s e ( p f ) ;
} e l s e
e x i t ( 1 ) ; }
Esercizio 4.3 ()
Scrittura e successiva lettura di strutture su file (versione 2).
Risoluzione
# i n c l u d e < s t d i o . h>
s t r u c t E l e m e n t o { l o n g m a t r i c o l a ; i n t m a t e r i e ; } ;
main ( ) {
FILE p f ; i n t i , num ;
s t r u c t E l e m e n t o v [ 4 ] , e l ; f o r ( i = 0 ; i < 4 ; i ++)
{
p f = f o p e n ( " n u m e r i . d a t " , " a +" ) ; i f ( p f )
{
p r i n t f ( " I n s e r i s c i l a m a t r i c o l a : " ) ; s c a n f ( "% l d " , & e l . m a t r i c o l a ) ;
p r i n t f ( " I n s e r i s c i i l numero d i e s a m i s u p e r a t i : " ) ; s c a n f ( "%d " , & e l . m a t e r i e ) ;
f w r i t e (& e l , s i z e o f ( s t r u c t e l e m e n t o ) , 1 , p f ) ; f c l o s e ( p f ) ;
} e l s e
e x i t ( 1 ) ; }
p f = f o p e n ( " n u m e r i . d a t " , " r " ) ; i f ( p f )
{
f r e a d ( v , s i z e o f ( s t r u c t E l e m e n t o ) , 4 , p f ) ; f c l o s e ( p f ) ;
} e l s e
e x i t ( 1 ) ;
f o r ( i = 0 ; i < 4 ; i ++)
p r i n t f ( " M a t r i c o l a : % l d \ t M a t e r i e : % d \ n " , v [ i ] . m a t r i c o l a , v [ i ] . m a t e r i e ) ; }
Esercizio 4.4 ()
Scrittura e successiva lettura di strutture su file (versione 3).
Risoluzione
# i n c l u d e < s t d i o . h>
s t r u c t E l e m e n t o { l o n g m a t r i c o l a ; i n t m a t e r i e ; } ;
main ( ) {
FILE p f ;
i n t i , num , oka y ;
s t r u c t E l e m e n t o e l ;
p r i n t f ( " Q u a n t i e l e m e n t i v u o i i n s e r i r e ? " ) ; s c a n f ( "%d " , & num ) ;
f o r ( i = 0 ; i <num ; i ++) {
p f = f o p e n ( " n u m e r i . d a t " , " a +" ) ; i f ( p f )
{
p r i n t f ( " I n s e r i s c i l a m a t r i c o l a : " ) ; s c a n f ( "% l d " , & e l . m a t r i c o l a ) ;
p r i n t f ( " I n s e r i s c i i l numero d i e s a m i s u p e r a t i : " ) ; s c a n f ( "%d " , & e l . m a t e r i e ) ;
f w r i t e (& e l , s i z e o f ( s t r u c t E l e m e n t o ) , 1 , p f ) ; f c l o s e ( p f ) ;
} e l s e
e x i t ( 1 ) ; }
p f = f o p e n ( " n u m e r i . d a t " , " r " ) ; i f ( p f )
{ do {
oka y = f r e a d (& e l , s i z e o f ( s t r u c t E l e m e n t o ) , 1 , p f ) ; i f ( oka y )
p r i n t f ( " M a t r i c o l a : % l d \ t M a t e r i e : % d \ n " , e l . m a t r i c o l a , e l . m a t e r i e ) ; } w h i l e ( oka y ) ;
f c l o s e ( p f ) ; }
e l s e e x i t ( 1 ) ; }
Esercizio 4.5 ()
Scrittura e successiva lettura di strutture su file mediante funzione.
Risoluzione
# i n c l u d e < s t d i o . h>
s t r u c t E l e m e n t o { l o n g m a t r i c o l a ; i n t m a t e r i e ; } ;
v o i d S a l v a ( c h ar n o m e f i l e , i n t numero ) ; v o i d L e g g i ( c h ar n o m e f i l e ) ;
v o i d main ( ) {
i n t num ;
p r i n t f ( " Q u a n t i e l e m e n t i v u o i i n s e r i r e ? " ) ; s c a n f ( "%d " , & num ) ;
S a l v a ( " e l e m e n t i . d a t " , num ) ; L e g g i ( " e l e m e n t i . d a t " ) ; }
v o i d S a l v a ( c h ar n o m e f i l e , i n t numero ) {
FILE p f ;
s t r u c t E l e m e n t o e l ;
i n t i ;
p f = f o p e n ( n o m e f i l e , "w" ) ; i f ( p f )
{
f o r ( i = 0 ; i < numero ; i ++) {
p r i n t f ( " I n s e r i s c i l a m a t r i c o l a : " ) ; s c a n f ( "% l d " , & e l . m a t r i c o l a ) ;
p r i n t f ( " I n s e r i s c i i l numero d i e s a m i s u p e r a t i : " ) ; s c a n f ( "%d " , & e l . m a t e r i e ) ;
f w r i t e (& e l , s i z e o f ( s t r u c t E l e m e n t o ) , 1 , p f ) ; }
f c l o s e ( p f ) ; }
e l s e e x i t ( 1 ) ; }
v o i d L e g g i ( c h ar n o m e f i l e ) {
FILE p f ;
s t r u c t E l e m e n t o e l ;
p f = f o p e n ( n o m e f i l e , " r " ) ; i f ( p f )
{
w h i l e ( f r e a d (& e l , s i z e o f ( s t r u c t e l e m e n t o ) , 1 , p f ) ! = 0 )
p r i n t f ( " M a t r i c o l a : % l d \ t M a t e r i e : % d \ n " , e l . m a t r i c o l a , e l . m a t e r i e ) ; f c l o s e ( p f ) ;
} e l s e
{
p r i n t f ( " E r r o r e d u r a n t e l ’ a p e r t u r a d e l f i l e " ) ; e x i t ( 1 ) ;
} }
4.2 fseek()
La funzionefseek()il cui prototipo è:
i n t f s e e k ( FILE f p , l o n g s p i a z z a m e n t o , i n t d a _ d o v e ) ;
setta l’indicatore di posizione del file per il flusso puntato dafp. La nuova posizione, misurata in bytes, è ottenuta aggiungendospiazzamentobytes alla posizione specificata dada_dove. Seda_doveè settato aSEEK_SET,SEEK_CUR, oSEEK_END, lo spiazzamento è relativo rispettivamente all’inizio del file, alla posizione attuale dell’indicatore, o alla fine del file.
4.3 ftell()
La funzioneftell()il cui prototipo è:
l o n g f t e l l ( FILE f p ) ;
restituisce la posizione corrente del file per il flusso puntato dafp.
Esercizio 4.6 ()
Determinare la dimensione di bytes di un file.
Risoluzione
# i n c l u d e < s t d i o . h>
main ( )
{
FILE p f ;
c h ar n o m e _ f i l e [ 1 2 8 ] ;
p r i n t f ( " I n s e r i s c i i l nome d i un f i l e ( p e r c o r s o c o m p l e t o ) : " ) ; s c a n f ( "% s " , n o m e _ f i l e ) ;
p f = f o p e n ( n o m e _ f i l e , " r " ) ; i f ( p f )
{
f s e e k ( p f , SEEK_END ) ;
p r i n t f ( "% s è l u n g o % l d b y t e s " , n o m e _ f i l e , f t e l l ( p f ) ) ; f c l o s e ( p f ) ;
} e l s e
{
p r i n t f ( "% s non e s i s t e ! " , n o m e _ f i l e ) ; }
}