INFORMATICA
I file
File
• Per rendere più uniforme possibile il comportamento dei
programmi in relazione alla grande varietà dei dispositivi
periferici, i linguaggi moderni fanno riferimento ad un
“modello”: i periferici sono visti come file o flusso
sequenziale, cioè come una struttura dati.
• Tuttavia nell’accezione più comune del termine il file è un
archivio di dati memorizzato su un dispositivo periferico,
normalmente il disco magnetico.
• Un file è una sequenza, teoricamente illimitata, di
componenti tutti dello stesso tipo. E’ una struttura ad
accesso sequenziale: per accedere all'n-esimo elemento
occorre accedere prima agli n-1 elementi che lo
precedono.
© Piero Demichelis
2
File
• In C i periferici sono visti come streams, cioè sequenze di
byte che fluiscono verso il programma (input streams) o
vengono generati dal programma (output streams).
• In particolare in C risultano predefiniti alcuni valori ai
quali fanno riferimento diretto le funzioni di I/O:
- stdin : periferico standard di input (associato alla tastiera);
- stdout : periferico standard di output (associato al monitor);
- stderr : periferico su cui inviare i messaggi d’errore
(generalmente associato al monitor).
© Piero Demichelis
3
File
• I file si distinguono in due categorie: file di tipo testo e file
binari.
• Nei file di tipo testo i dati, che all’interno dell'elaboratore
sono codificati in binario, vengono trasferiti all'esterno come
sequenze di caratteri, dopo aver subito una conversione (da
floating point a decimale, da complemento a 2 a decimale,
ecc.).
• Nei file binari i dati sono trasferiti all'esterno come pacchetti
di bit senza alcuna manipolazione (i dati sono pertanto
un’immagine perfetta della loro rappresentazione in
memoria).
© Piero Demichelis
4
File
• Mentre i file di tipo testo si possono visualizzare e
stampare, i file binari si possono solo conservare e
rileggere.
• Nei file di tipo testo sono previste informazioni esplicite di
“fine linea ” (new-line, lo stesso carattere utilizzato per
l’output sul monitor) e di “fine file ” (end-of-file)
• Sostanzialmente scrivere su un file di tipo testo è assai
simile a scrivere sul monitor così come leggere da un file
di testo corrisponde a leggere da tastiera.
© Piero Demichelis
5
Funzione fopen
• Per poter operare sui file, siano essi dispositivi periferici o
file su disco, occorre instaurare una connessione che
renda possibile la comunicazione tra programma e file:
questa predisposizione è detta apertura del file.
• Lo standard del C prevede che gli stream standard (stdin,
stdout, stderr ) siano sempre predisposti e non
necessitino quindi di essere “aperti”.
• Gli altri file possono essere utilizzati solo dopo averli
aperti mediante la funzione di libreria fopen la quale
effettua l'aggancio con il file e restituisce un certo
numero di informazioni inerenti il file stesso, in una
struttura dati a cui si può accedere tramite un puntatore.
© Piero Demichelis
6
Funzione fopen
• Per questo, prima di chiamare la fopen, occorre dichiarare un
“puntatore al file”, utilizzando una struttura predefinita in stdio.h
come nel seguente esempio:
FILE *miofile;
dove miofile è un puntatore ad un file.
• FILE deve essere scritto in maiuscolo: in realtà si tratta di una
struttura definita nel file stdio.h.
• La chiamata della fopen ha la seguente forma:
fopen ("nome_file ", "tipo_apertura");
© Piero Demichelis
7
Funzione fopen
• “nome_file ” è una stringa contenente il nome del file nel
formato richiesto dal sistema che si utilizza (ad esempio,
per MSDOS è un nome preceduto dal “path ”, come
"C:\\WORK\\MIOFILE.DAT", dove il doppio backslash è la
notazione richiesta dal C per il singolo backslash);
• “tipo_apertura ” è una stringa che specifica il tipo di
azioni che si vogliono compiere sul file e può assumere i
valori seguenti:
r : apre un file già esistente in lettura (for reading).
w : crea ed apre un file in scrittura (for writing): se il file non
esiste, lo crea; se esiste, lo rimpiazza (attenzione: il contenuto del
file precedente viene irrimediabilmente perso);
a : apre un file per aggiungere dati (for appending): i dati
vengono aggiunti in coda ad un file esistente oppure viene creato
un nuovo file;
© Piero Demichelis
8
Funzione fopen
r+ : apre un file in lettura e scrittura;
w+ : crea ed apre un file in lettura e scrittura: se il file esiste già,
viene rimpiazzato;
a+ : apre un file per leggere ed aggiungere: i dati vengono
aggiunti in coda ad un file esistente oppure viene creato un nuovo
file.
• La fopen restituisce l'indirizzo di una struttura di tipo
FILE (puntatore al file ) se l'operazione è andata bene; se
invece si è verificato qualche inconveniente (file
inesistente, disco protetto in scrittura, disco pieno, ecc.),
restituisce un puntatore con valore NULL.
© Piero Demichelis
9
Funzione fopen
• Esempio di apertura di un file:
FILE *inp_file;
...........................
/* dichiarazione del puntatore al file */
/*
apertura: se capita errore scrive il messaggio su stderr
*/
if ((inp_file = fopen("C:\\WORK\\MIOFILE.DAT", "r") == NULL)
{
fprintf (stderr, "Errore apertura MIOFILE.DAT in input\n");
/* oppure printf ( “Errore apertura MIOFILE.DAT in input \n”);
*/
}
else
/* apertura completata con successo: ora si può operare sul file */
.........................
© Piero Demichelis
10
Funzione fclose
• Al termine delle operazioni, è necessario che il file venga
“chiuso”: si utilizza per questa operazione la funzione
fclose che ha come parametro il puntatore al file.
• Sintassi:
fclose (puntatore);
puntatore : puntatore al file aperto in precedenza con la
fopen
•
La fclose restituisce un valore intero pari a 0 se la
chiusura è avvenuta correttamente, pari a EOF in caso di
errore.
© Piero Demichelis
11
Funzione fclose
• Questa funzione non solo libera la struttura che era stata
creata per immagazzinare le informazioni relative a quel
file, ma effettua le operazioni di “completamento ” e
“chiusura ” del file.
• Le operazioni di “completamento ” assumono rilevanza
quando il file è in scrittura: infatti le operazioni fisiche di
scrittura dei dati non vengono eseguite immediatamente:
i dati vengono prima immagazzinati in appositi buffer di
memoria gestiti dal sistema operativo.
• Il trasferimento al dispositivo esterno avviene solo
quando in questi buffer si forma almeno un “blocco ”
completo di dati da scrivere (ad esempio un settore del
disco).
© Piero Demichelis
12
Funzione fclose
• Può succedere che quando il programma completa le
operazioni di scrittura sul file siano ancora presenti in
memoria (nei buffer) dei dati che devono essere trasferiti
sul disco prima di poter chiudere definitivamente il file
“fisico”.
• Questa operazione è nota col termine di flush del file: la
funzione fclose svolge appunto questo compito.
• Le vere e proprie operazioni di “chiusura ” del file sono
demandate al sistema operativo (registrazione del file nel
proprio direttorio, “rilascio” del file se era bloccato, ecc.).
© Piero Demichelis
13
Lettura e scrittura di file di testo
• Quando nella fopen manca l'esplicita richiesta che il file
sia di tipo binario (aggiunta della lettera b nella stringa
“tipo_apertura ”), il file viene aperto by default di tipo
testo.
• Per leggere e scrivere sui dispositivi non standard, quelli
cioè che devono essere aperti con la fopen, e quindi,
ovviamente, sui file residenti su disco, si devono usare
funzioni con lo stesso nome di quelle degli standard
streams ma precedute dalla lettera f.
• Pertanto a scanf corrisponde fscanf, a printf corrisponde
fprintf, ecc.
• Occorre inoltre specificare il puntatore al file su cui
devono operare.
© Piero Demichelis
14
Formato delle funzioni di I/O su file
inp_file = fopen (“nome”, “r”);
out_file = fopen (“nome”, “w”);
/* apertura in lettura */
/* apertura in scrittura */
fscanf (inp_file, “format", lista variabili);
fprintf (out_file, “format”, lista variabili);
/* lettura da inp_file */
/* scrittura su out_file */
carattere = fgetc (inp_file);
/* lettura di un carattere da inp_file */
fputc (carattere, out_file);
/* scrittura di un carattere su out_file */
fgets (stringa, n, inp_file); /* legge una riga (fino a n car.) in stringa */
fputs (stringa, out_file);
/* scrive stringa in out_file */
fclose (inp_file);
fclose (out_file);
/* chiusura dei file */
© Piero Demichelis
15
Funzione feof
• Nella lettura di un file, spesso non si conosce con precisione quanti
dati contiene.
• D’altra parte un tentativo di lettura oltre la fine del file (oltre l’ultimo
record) provoca un errore irrimediabile e il programma viene
interrotto.
• E’ necessario dunque sapere quando ci si deve fermare nella lettura,
ovvero quando si sta leggendo l’ultimo record.
• A questo scopo il C possiede la funzione
feof (puntatore)
che restituisce il valore vero quando il puntatore è posizionato
sull’EOF, falso in caso contrario.
© Piero Demichelis
16
Funzioni di I/O su file
• Tutte le funzioni che operano su file richiedono
ovviamente il puntatore al file.
• Queste funzioni si comportano alla stessa stregua di
quelle operanti sugli standard streams, anche nei
confronti dei fine linea e fine file.
• Non deve trarre in inganno il fatto che il linguaggio tratti
le informazioni di fine linea e di fine file come caratteri:
ad esempio, non è detto che per tutti i sistemi operativi la
terminazione dei file di tipo testo sia costituita proprio da
un carattere.
• Per questo motivo queste informazioni sono state definite
con valori simbolici (EOF, \n)!
© Piero Demichelis
17
Esempio
• Esempio: programma che richiede il nome di un file, lo crea, lo
riempie con i valori interi da 1 a 10 (uno per record) e
successivamente ne visualizza il contenuto.
#include <stdio.h>
#include <stdlib.h>
#define NUM_DATI 10
main()
{
int dato, ndati;
FILE *fdati;
char nomefile[50];
printf (“\nIntroduci il nome del file: ");
scanf ("%s", nomefile);
© Piero Demichelis
18
Esempio
if ((fdati = fopen (nomefile, "w")) == NULL)
/* crea il file */
{
printf ("\nErrore apertura del file %s", nomefile);
exit (0);
}
for (dato = 1; dato <= NUM_DATI; dato++)
{
fprintf (fdati, "%d", dato);
/* scrive un dato nel file */
if (dato != NUM_DATI) /* \n per tutte le righe tranne l’ultima */
fprintf (fdati, "\n");
}
fclose (fdati);
/* chiude il file */
© Piero Demichelis
19
Esempio
printf ("\nVisualizza il contenuto del file\n");
if ((fdati = fopen (nomefile, "r")) == NULL)
{
printf ("\nErrore apertura del file %s", nomefile);
exit (1);
}
while (!feof (fdati))
{
fscanf (fdati, "%d", &dato);
printf ("%d\n", dato);
}
/* apre il file */
/* Finché non si raggiunge EOF */
/* legge un dato dal file */
/* visualizza il dato sul monitor */
fclose (fdati);
}
/* chiude il file */
© Piero Demichelis
20
Schema generale di apertura di un file
• Spesso nei programmi deve essere elaborato più di un file. Lo
schema proposto in precedenza (a proposito della fopen) per
l’apertura potrebbe in qualche caso risultare scomodo.
• Ricordando che il main in definitiva è una funzione (l’unica che può
avere quel nome), possiamo strutturare l’apertura nel modo
seguente:
FILE *inp_file;
/* dichiarazione del puntatore al file */
...........................
if ((inp_file = fopen (nomefile, "r") == NULL)
{
printf (“\nErrore in apertura di %s”, nomefile);
exit (0);
}
/* apertura completata con successo: ora si può operare sul file */
© Piero Demichelis
21
Schema generale di lettura da file
• Le lettura di un file è in genere regolata mediante un
ciclo:
leggi un dato dal file;
finché (non è finito il file )
{
processa il dato;
leggi un dato dal file;
}
• La condizione “non è finito il file ” può essere realizzata
in vari modi:
- usando i valori restituiti dalle funzioni di input;
- usando feof.
© Piero Demichelis
22
Schema di lettura da file
• Lettura di un file formattato (es. un intero per riga).
while ( !feof (fp))
{
fscanf (fp, “%d”, &val);
elabora il dato (val)
}
res = fscanf (fp, “%d”, &val);
while (res != EOF)
{
elabora il dato (val)
res = fscanf (fp, “%d”, val);
}
© Piero Demichelis
23
Lettura da file
•
Sfruttando il fatto che fscanf restituisce il numero di valori
letti correttamente è possibile usare quale condizione del
while direttamente la funzione o, meglio, il valore che la
funzione restituisce, così:
while ( fscanf (fp, “%d”, &val) != EOF )
{
elabora val;
}
• In questo modo non è necessario eseguire una lettura
preventiva prima di entrare nel ciclo!
© Piero Demichelis
24
Esempio
• Nel file estremi.dat sono registrate coppie di numeri interi
(x, y), una per riga. Leggere le coppie di numeri e
scrivere in un secondo file diff.dat le differenze (x – y),
una per riga.
• Esempio:
diff.dat
estremi.dat
23
2
19
23
3
…
-9
-9
13
18
1
…
32
11
6
5
2
…
© Piero Demichelis
25
Esempio
#include <stdio.h>
int main()
{
FILE *fpin, *fpout;
int x, y;
/*
apertura del primo file
*/
if ((fpin = fopen (“estremi.dat”, ”r”)) == NULL)
{
fprintf (stderr, “Errore nell’apertura di estremi.dat\n”);
exit (0);
}
© Piero Demichelis
26
Esempio
/* apertura del secondo file */
if (( fpout = fopen (“diff.dat”, ”w”)) == NULL)
{
fprintf (stderr, “Errore nell’apertura di diff.dat\n”);
exit (1);
}
while ( fscanf (fpin, “%d%d”, &x, &y) != EOF)
{
/*
ora ho a disposizione x e y
*/
fprintf (fpout, “%d\n”, x - y);
}
fclose (fpin);
fclose (fpout);
}
© Piero Demichelis
27
Avvertenza
• E’ in generale sbagliato tentare di memorizzare il
contenuto di un file in un vettore.
•
La dimensione (numero di righe o di dati) di un file non è
quasi mai nota a priori e un file è, per definizione, una
“sequenza illimitata di dati”.
•
Tuttavia, anche se la dimensione è nota, tipicamente è
molto grande.
© Piero Demichelis
28
Scrittura di un file
• La struttura di scrittura di un file non è diversa dalla
struttura di lettura: si tratterà tipicamente di un ciclo che
scrive un certo numero di dati ad ogni iterazione.
• Non è necessario scrivere EOF. Infatti la “marca” di EOF è
automaticamente scritta dal linguaggio alla fine della
scrittura del record corrente.
• E’ invece importante il formato dei dati. Se si scrive un
file è perché prima o poi si desidera rileggerlo! Occorre
dunque che i dati siano scritti in modo compatibile con le
istruzioni di lettura (ad esempio inserire almeno uno
spazio tra un dato e il successivo, ecc.)
© Piero Demichelis
29
Scarica

File