Il linguaggio C
Input e output
I flussi di I/O
L’uso di buffer
Apertura e chiusura di file
Lettura e scrittura di dati
La selezione di un metodo di I/O
I/O non bufferizzato
L’accesso diretto a file
Input e output
1
2
Input / Output
Introduzione
La gestione dell’I/O su file è uno degli aspetti più complessi dei
linguaggi di programmazione, perché strettamente correlato con
le particolari modalità di accesso a file ed alle periferiche dettate
dal sistema operativo difficoltà nella progettazione di servizi di
I/O portabili
Storicamente, l’apposita libreria di run−time del C e la libreria di
I/O di UNIX erano parzialmente sovrapposte: tuttavia, la libreria
C tratta l’I/O bufferizzato
bufferizzato, contrariamente alla libreria UNIX
Le funzioni ANSI di I/O sono tutte bufferizzate (con possibilità di
modifica della dimensione del buffer)
Le funzioni ANSI di I/O operano inoltre una distinzione tra
accesso a file in modalità binaria o testuale; in ambiente UNIX la
distinzione è poco significativa poiché UNIX tratta file binari e file
di testo alla stessa stregua  come sequenze di byte (in altri
sistemi operativi, tale distinzione è invece importante)
Spesso un programma deve acquisire dati da una sorgente esterna
o inviare informazioni a una destinazione esterna.
L’informazione può essere di ogni tipo:
caratteri,
immagini,
suoni,
ecc.
3
4
Stream di Input / Output
Stream Input / Output
Per acquisire dati, un programma apre uno stream (o flusso)
associato alla sorgente di informazione (file, socket, memoria, ecc.)
e legge l’informazione sequenzialmente da esso.
In modo analogo, un programma può inviare verso una
destinazione esterna dei dati aprendo uno stream associato alla
destinazione e scrivendovi in modo sequenziale i dati.
Indipendentemente dall’origine e dalla destinazione dei dati e dal
loro tipo, gli algoritmi per leggere e scrivere sequenzialmente dei
dati sono gli stessi.
5
I flussi di I/O − 1
Input / Output
Lettura
Apertura dello stream
Finché e’ presente informazione
lettura dell’informazione
Chiusura dello stream
6
Il linguaggio C non distingue tra periferiche e file su disco:
in entrambi i casi le operazioni di I/O sono realizzate
attraverso flussi o stream di I/O associati a file o periferiche
Un flusso di I/O è una sequenza ordinata di byte: la lettura
e la scrittura di un file o di una periferica implicano la
lettura/scrittura di dati da/su un flusso
Scrittura
Apertura dello stream
Finché e’ presente informazione
scrittura dell’informazione
Chiusura dello stream
Sorgente C
Flusso
File
I flussi di I/O
I/O:: i programmi C accedono ai dati memorizzati in file
attraverso array monodimensionali di caratteri, detti flussi di I/O
7
8
I flussi di I/O − 2
I flussi di I/O − 3
Per eseguire l’I/O, è necessario associare un flusso ad un file o a una
periferica
occorre dichiarare un puntatore alla struttura FILE
La struttura FILE
FILE, definita in stdio.
stdio.h, è costituita da campi che
contengono informazioni quali il nome del file, la modalità di accesso,
il puntatore al prossimo carattere nel flusso
I campi della struttura FILE
FILE, istanziati all’atto dell’apertura di un
flusso (o in fase di utilizzo), sono dipendenti dall’implementazione e
differiscono per sistemi operativi diversi
Le strutture FILE forniscono al sistema operativo le
informazioni necessarie per la gestione dei file: l’unico
meccanismo di accesso ad un flusso è il puntatore alla
struttura FILE
FILE, detto puntatore a file
Il puntatore a file deve essere dichiarato nel programma,
contiene l’identificatore del flusso restituito da una chiamata
alla fopen(), e viene utilizzato per leggere, scrivere e chiudere
il flusso
Ciascun programma può aprire più flussi simultaneamente, nel
rispetto dei limiti imposti dalla particolare implementazione
FILE *fopen(char *filename, char *mode);
9
I flussi di I/O − 4
10
I flussi standard
Uno dei campi della struttura FILE è un indicatore di posizione
nel file
file, che punta al successivo byte da leggere o scrivere: a
fronte di operazioni di lettura/scrittura, il sistema operativo
modifica conseguentemente l’indicatore di posizione
L’indicatore di posizione del file non può essere manipolato
direttamente (in maniera portabile), ma può essere letto e
modificato tramite funzioni di libreria, permettendo accessi al
flusso non sequenziali
Attenzione il puntatore a file identifica un flusso aperto, che è
Attenzione:
connesso ad un file o a una periferica, l’indicatore di posizione
nel file identifica uno specifico byte all’interno di un flusso
11
Esistono tre flussi standard,
standard
che vengono aperti
stdin, stdout
stdout, stderr
automaticamente per ogni programma: stdin
I flussi standard sono connessi al terminale, per default, ma
molti sistemi operativi ne permettono la redirezione (ad es.,
è possibile inviare messaggi di errore ad un file per
effettuare diagnostica)
Le funzioni printf() e scanf() utilizzano i flussi standard di
I/O; possono essere utilizzate anche per fare I/O su/da file,
ridirigendo stdin e stdout a file per mezzo della funzione
freopen()
Tuttavia, esistono le funzioni di libreria apposite, fprintf() e
fscanf(), che permettono di specificare il flusso su cui
operare
12
L’uso del buffer − 1
I formati testo e binario
Confrontate con la memoria centrale, le unità di memoria di massa
sono estremamente lente: nei programmi, il tempo richiesto per
accedere alle periferiche eccede largamente il tempo impiegato
dalla CPU per i calcoli effettivi
È di fondamentale importanza ridurre il numero di operazioni fisiche
di lettura/scrittura, mediante tecniche di bufferizzazione
Un buffer è un’area dei memoria in cui i dati sono memorizzati
temporaneamente, prima di essere inviati a destinazione
Mediante l’uso di buffer, il sistema operativo può limitare il numero
di accessi alla memoria di massa
Tutti i sistemi operativi utilizzano buffer per leggere/scrivere su
unità di I/O: l’accesso avviene con “granularità di blocco”, con
blocchi di dimensione 512/4096 byte
I dati possono essere acceduti in modalità testo o binaria
binaria:
Un flusso testuale è composto da una sequenza di linee,
concluse da newline (sistemi operativi diversi possono
memorizzare linee con formati diversi, utilizzando un carattere
differente di terminazione linea)
I flussi standard sono testuali
In formato binario il compilatore non effettua alcuna
interpretazione dei byte: i bit sono letti e scritti come un flusso
continuo
I flussi binari sono utilizzati quando è fondamentale preservare
l’esatto contenuto del file
13
L’uso del buffer − 2
14
L’uso del buffer − 3
Le librerie standard di I/O del C comprendono un gestore di buffer
che mantiene il buffer in memoria il più a lungo possibile: se si
accede alla stessa porzione di un flusso più volte, si ha alta
probabilità che il flusso sia stato mantenuto nella memoria centrale
(si possono verificare problemi di accesso concorrente, gestibili via
sistema operativo, se il file è condiviso da più processi)
Sia nel caso di bufferizzazione a righe che a blocchi, è possibile
richiedere esplicitamente al sistema operativo di forzare l’invio del
buffer a destinazione in un momento qualsiasi, per mezzo della
funzione fflush()
Il C consente di personalizzare il meccanismo di bufferizzazione
(modificando le dimensioni del buffer) fino ad eliminarla, ponendo
la dimensione del buffer a zero
Le librerie di run−time del C contengono un ulteriore livello di
bufferizzazione che può assumere due forme distinte:
bufferizzazione a linee e bufferizzazione a blocchi
Nella bufferizzazione a linee, il sistema immagazzina i caratteri
fino a quando si incontra un newline (o il buffer è pieno),
inviando l’intera linea al sistema operativo (ciò che accade per
l’inserimento da tastiera)
Nella bufferizzazione a blocchi, il sistema immagazzina i
caratteri fino a riempire un blocco, trasferendolo quindi al
sistema operativo
Tutti i flussi di I/O a file utilizzano una bufferizzazione a
blocchi, i flussi riferiti a terminale sono dipendenti dal sistema
operativo, ma sono o non bufferizzati o bufferizzati a linee
15
16
Apertura e chiusura di file − 1
Il file header <stdio.h>
Prima di poter accedere al contenuto di un file, è necessario aprirlo
tramite la funzione fopen(), che prevede due parametri: il nome del
file e la modalità di accesso
Esistono due insiemi di modalità di accesso, per i flussi testuali e
per i flussi binari
Per utilizzare le funzioni di I/O è necessario includere il file
header stdio
stdio..h che contiene:
Le dichiarazioni dei prototipi di tutte le funzioni di I/O
La dichiarazione della struttura FILE
Le macro costanti, come stdin
stdin, stdout
stdout, stderr
stderr, EOF
EOF corrisponde al valore restituito dalle funzioni di I/O in
corrispondenza dell’identificatore di fine file
Nota La definizione di NULL
Nota:
NULL, per l’ANSI C, è invece
stddef..h
contenuta nel file stddef
#ifndef NULL
#define NULL (void *) 0
#endif
Possibilità di lettura del flusso
Possibilità di scrittura sul flusso
Possibilità di scrittura solo alla fine
r+
+
w+
+
*
*
*
*
*
“a
a”
Apre un file testuale esistente in modalità append ; la scrittura può avvenire solo
alla fine del file; se il file non esiste verrà creato automaticamente, in caso
contrario il contenuto del file preesistente verrà mantenuto
“rr+”
Apre un file testuale esistente in lettura e scrittura, posizionandosi all’inizio del file;
se il file non esiste, la funzione ritornerà il codice di errore NULL
“w
w+ ”
Crea un nuovo file testuale e lo apre in lettura e scrittura
“a
a+”
Apre un file testuale esistente o ne apre uno nuovo in modalità append; la lettura
può avvenire in una posizione qualsiasi del file, la scrittura solo alla fine
18
FILE *open_test()
{
/* restituisce un puntatore ad una struttura FILE */
FILE *fp;
a+
+
*
*
Il file preesistente viene reinizializzato
Crea un nuovo file testuale e lo apre in scrittura, posizionandosi all’inizio del file; se
il file esiste, i dati precedenti vengono eliminati
Esempio: Funzione che apre un file testuale, denominato test,
Esempio
test
con accesso in lettura
Note
La funzione fprintf() è
#include <stdio.h>
analoga
alla
printf(),
#include <stddef.h>
Proprietà di file e flussi rispetto alle modalità di apertura della fopen()
*
“w
w”
Apertura e chiusura di file − 3
Le modalità binarie differiscono per l’aggiunta di una b (es.,
“rb
rb”)
La funzione fopen() restituisce un puntatore a file, utilizzabile
per accedere successivamente al file aperto
Il file deve esistere prima dell’apertura
Apre un file testuale esistente in lettura, posizionandosi all’inizio del file; se il file
non esiste, la funzione ritornerà il codice di errore NULL
17
Apertura e chiusura di file − 2
r w a
“rr”
*
*
*
*
fp = fopen(“test”, “r”);
if (fp == NULL)
fprintf(stderr, “Errore nell’apertura del file test\n”);
return fp;
*
*
}
*
19
eccettuato un parametro
aggiuntivo, che identifica il
flusso di uscita
La funzione open_test() è
poco
flessibile,
perché
permette l’apertura del solo
file test e soltanto per
accessi in lettura
20
Apertura e chiusura di file − 4
Apertura e chiusura di file − 5
Per chiudere un file viene utilizzata la funzione fclose(), con
argomento il puntatore alla struttura FILE da chiudere
La chiusura del file provoca il rilascio della struttura FILE,
FILE per
la successiva allocazione ad altri file
La chiusura del file forza anche la scrittura del contenuto del
buffer associato al flusso
Dato che tutti i sistemi operativi stabiliscono un numero
massimo di flussi aperti contemporaneamente, è buona
norma chiudere i file quando se ne è conclusa l’elaborazione
Tutti i flussi aperti vengono comunque chiusi dal sistema
operativo quando il programma termina correttamente (nel
caso di terminazione anomala, il comportamento non è
standardizzato)
#include <stdio.h>
#include <stddef.h>
FILE *open_file(file_name, access_mode)
char *file_name, *access_mode;
{
FILE *fp;
if ((fp = fopen(file_name, access_mode)) == NULL)
fprintf(stderr, “Errore nell’apertura del file %s \
con modalità di accesso %s\n”, file_name, access_mode);
return fp;
}
La funzione open_file() è equivalente alla fopen(), a meno
della notifica di errore se il file non può essere aperto
21
22
Lettura e scrittura per caratteri − 1
Lettura e scrittura per caratteri − 2
Esempio: Funzione che copia il contenuto di un file in un altro
Esempio
Esistono quattro modalità per la lettura/scrittura di caratteri
da un flusso
getc() : macro che legge un carattere da un flusso
fgetc() : analoga a getc(), ma realizzata come una funzione
putc() : macro che scrive un carattere su un flusso
fputc() : analoga a putc(), ma realizzata come una funzione
Le macro getc() e putc(), in quanto tali, sono normalmente
molto più veloci delle analoghe funzioni
Tuttavia, se un parametro attuale contiene operatori che
implicano effetti collaterali, fgetc() ed fputc() sono preferibili
Per le altre macro di libreria, lo standard ANSI prevede che
gli argomenti possano comparire una sola volta nel corpo
della macro, per evitare effetti collaterali
#include <stdio.h>
#include <stddef.h>
#define FAIL 0
#define SUCCESS 1
int copy_file(infile, outfile)
char *infile, *outfile;
{
FILE *fp1, *fp2;
char c;
if ((fp1 = fopen(infile, “r”)) == NULL)
return FAIL;
if ((fp2 = fopen(outfile, “w”)) == NULL)
{
fclose(fp1);
return FAIL;
}
while ((c = getc(fp1)))
putc(c, fp2);
fclose(fp1);
fclose(fp2);
return SUCCESS;
23
}
Note
Entrambi i file vengono acceduti
in modalità binaria
La macro getc
getc()
() legge il
prossimo carattere dal flusso
specificato e sposta l’indicatore
di posizione del file avanti di un
elemento ad ogni chiamata
In modalità binaria, non è
possibile interpretare il valore di
ritorno
della
getc()
getc
()
per
decretare la fine del file: feof
feof()
()
invece non presenta ambiguità
24
Lettura e scrittura per linee − 1
Lettura e scrittura per linee − 2
Esistono due funzioni di I/O orientate alle linee, fgets
fgets()
() ed
fputs()
fputs
()
Il prototipo per la funzione fgets
fgets()
() è
char *fgets(
*fgets(char *s
*s,
, int n, FILE *stream);
*stream);
Gli argomenti hanno il seguente significato:
char *fputs
*fputs(
(char *s,
*s, FILE *stream
*stream);
);
La funzione fputs
fputs()
() scrive l’array identificato dal primo
argomento nel flusso identificato dal secondo argomento
char *gets
*gets(
(char *s);
*s);
s
: puntatore al primo elemento dell’array in cui vengono
memorizzati i caratteri letti
n
: numero massimo dei caratteri da leggere (compreso ‘\0’)
stream : puntatore al flusso da cui leggere
La funzione fgets
fgets()
() legge caratteri fino ad un newline,
newline la fine del
file o il numero massimo specificato di caratteri, inserendo
automaticamente un carattere NULL dopo l’ultimo carattere
scritto nell’array; restituisce NULL se incontra la fine del file,
altrimenti restituisce il primo argomento
La differenza fondamentale fra gets
gets()
() (che legge da stdin)
stdin ed
fgets()
fgets
() è che la seconda inserisce nell’array (prima del \0
finale) anche il carattere di newline che delimita la linea
Inoltre fgets
fgets()
() permette la specifica del numero massimo dei
gets()
() procede sempre fino ad
caratteri da leggere, mentre gets
un terminatore (newline
newline o EOF)
EOF
25
Lettura e scrittura per linee − 3
#include <stdio.h>
#include <stddef.h>
#define FAIL 0
#define SUCCESS 1
#define LINESIZE 100
int copy_file(infile, outfile)
char *infile, *outfile;
{
FILE *fp1, *fp2;
char line[LINESIZE];
if ((fp1 = fopen(infile, “r”)) == NULL)
return FAIL;
if ((fp2 = fopen(outfile, “w”)) == NULL)
{
fclose(fp1);
return FAIL;
}
while (fgets(line, LINESIZE, fp1) != NULL)
fputs(line, fp2);
fclose(fp1);
fclose(fp2);
return SUCCESS;
}
26
Lettura e scrittura per blocchi − 1
Note
Il file è aperto in modalità testuale per
accedere ai dati a livello di linea,
altrimenti fgets() potrebbe operare in
modo scorretto, cercando caratteri di
newline non presenti nel file (il
terminatore di linea può essere diverso
per sistemi operativi diversi)
fgets(), invece, si “adegua” alle
caratteristiche del sistema operativo
specifico
La funzione copy_file() per linee è più
lenta della versione per caratteri, poiché
fgets() ed fputs() sono realizzate per
mezzo di fgetc() ed fputc() (meno
efficienti delle analoghe macro)
27
Un blocco può essere immaginato come un array: quando si
legge o si scrive un blocco è necessario specificare il numero di
elementi del blocco e la dimensione di ognuno di essi
Le due funzioni orientate alla gestione dei blocchi sono fread()
e fwrite()
Il prototipo per la funzione fread() è
int fread(void *ptr, int size, int nmemb, FILE *stream);
Gli argomenti hanno il seguente significato:
ptr
: puntatore ad un array in cui vengono memorizzati i dati
size
: dimensione di ogni elemento dell’array
nmemb : numero di elementi da leggere
stream : puntatore a file
28
Lettura e scrittura per blocchi − 3
Lettura e scrittura per blocchi − 2
#include <stdio.h>
#include <stddef.h>
#define FAIL 0
#define SUCCESS 1
#define BLOCKSIZE 512
typedef char DATA;
La funzione fread() restituisce il numero di elementi
effettivamente letti, che dovrebbe coincidere con il terzo
argomento, a meno di errori o di condizioni di fine file
La funzione fwrite() ha gli stessi argomenti, ma scrive nel
flusso gli elementi contenuti nell’array
La funzione copy_file() può essere realizzata anche con
granularità di blocco:
if (ferror(fp1))
{
printf(“Errore in lettura del file %s\n”, infile);
fclose(fp1);
fclose(fp2);
return FAIL;
}
{
FILE *fp1, *fp2;
DATA block[BLOCKSIZE];
int num_read;
29
La selezione di un metodo di I/O − 1
Le macro putc() e getc() sono le più veloci, ma la maggior
parte dei sistemi operativi è in grado di realizzare operazioni di
I/O su blocchi ancora più efficienti (ad es., read() e write() in
UNIX)
#include <stdio.h>
Funzione che conta il
numero di linee di un file
fwrite(block, sizeof(DATA), num_read, fp2);
int copy_file(infile, outfile)
char *infile, *outfile;
La condizione di fine file è controllata confrontando il numero
degli elementi letti, restituito da fread(), con il valore
specificato nella lista degli argomenti: se sono diversi si ha una
condizione di fine file o di errore
La funzione ferror() viene utilizzata per stabilire quale
condizione si è verificata
Talvolta occorre privilegiare la
semplicità all’efficienza: fgets()
e fputs(), ad esempio, sono
lente,
ma
particolarmente
adatte nei casi in cui sia
necessario analizzare linee
while ((num_read = fread(block, sizeof(DATA),
BLOCKSIZE, fp1)) == BLOCKSIZE)
fwrite(block, sizeof(DATA), num_read, fp2);
#include <stddef.h>
#define MAX_LINE_SIZE 120
if ((fp1 = fopen(infile, “rb”)) == NULL)
{
printf(“Errore nell’apertura del file %s \
in input\n”, infile);
return FAIL;
}
if ((fp2 = fopen(outfile, “wb”)) == NULL)
{
printf(“Errore nell’apertura del file %s \
in output\n”, outfile);
fclose(fp1);
return FAIL;
}
fclose(fp1);
fclose(fp2);
return SUCCESS;
}
30
La selezione di un metodo di I/O − 2
Ultimo, ma non meno importante, fattore da considerare
nella scelta di un metodo di I/O è la portabilità, fondamentale
non tanto nella scelta del tipo di I/O (granularità a caratteri,
linee o blocchi), ma nella scelta della modalità testo o binaria
Se il file contiene dati testuali (codice sorgente o documenti), la
modalità testo e l’accesso per linee sono da privilegiare
int lines_in_file(fp)
FILE *fp;
{
char buf[MAX_LINE_SIZE];
int line_num = 0;
rewind(fp); /* sposta l’indicatore di posizione
all’inizio del file */
while (fgets(buf, MAX_LINE_SIZE, fp) != NULL)
line_num++;
return line_num;
Se i dati sono numerici e non sono strutturati per linee, è
preferibile la modalità binaria, con accesso per caratteri o per
blocchi (codice eseguibile)
}
31
32
I/O non bufferizzato − 1
I/O non bufferizzato − 2
Le librerie di run−time del C consentono di modificare la
dimensione del buffer: tale possibilità deve essere utilizzata
con attenzione, dato che la dimensione del buffer dovrebbe
essere “ottima” per il particolare sistema operativo
Talvolta, è tuttavia necessario eliminare completamente la
bufferizzazione, tipicamente quando si vogliono elaborare
immediatamente i dati di input
Per eliminare la bufferizzazione ci si può avvalere delle
funzioni setbuf() e setvbuf()
La funzione setbuf() richiede due parametri: un puntatore a
file, ed un puntatore ad un array di caratteri da utilizzare
come nuovo buffer; se tale puntatore è nullo, la
bufferizzazione viene eliminata; setbuf() non restituisce valori
La funzione setvbuf() richiede due parametri aggiuntivi, che
permettono di specificare la tipologia di bufferizzazione (per
linee, per blocchi, o assente) e la dimensione dell’array da
utilizzare come buffer
La tipologia di bufferizzazione va specificata mediante uno
dei tre simboli definiti in stdio.
stdio.h:
_IOFBF : bufferizzazione per blocchi
_IOLBF : bufferizzazione per linee
_IONBF : bufferizzazione assente
La funzione restituisce un valore diverso da zero se
completata correttamente, zero se non è in grado di
soddisfare la richiesta
setbuf(stdin, NULL);
setvbuf(stdin, NULL, _IONBF, 0);
33
L’accesso diretto a file − 1
34
L’accesso diretto a file − 2
In C, le funzioni per l’accesso diretto a file sono fseek() e
Esempio: L’istruzione
Esempio
stat = fseek(fp, 10, SEEK_SET);
sposta l’indicatore di posizione del file al carattere 10 del flusso
(l’undicesimo), che sarà il prossimo elemento letto o scritto
La fseek() restituisce zero se la richiesta è corretta, un valore
diverso da zero altrimenti
Esempio: L’istruzione
Esempio
stat = fseek(fp, 1, SEEK_END);
non è lecita se fp è aperto in sola lettura, perché sposta
l’indicatore oltre la fine del file
Per flussi binari, lo spostamento può essere un qualsiasi numero
intero che non sposti l’indicatore al di fuori del file; per flussi
testuali, deve essere zero o un valore restituito dalla ftell()
ftell()
La funzione fseek() sposta l’indicatore di posizione del file a
un carattere specificato nel flusso
Il prototipo della fseek() è
int fseek(FILE *stream, long int offset, int whence)
dove:
stream : puntatore a file
offset : numero di caratteri di spostamento
whence : posizione di partenza da cui calcolare lo spostamento
L’argomento whence può assumere uno dei tre seguenti
valori, definiti in stdio.
stdio.h
SEEK_SET : inizio del file
SEEK_CUR : posizione corrente dell’indicatore
SEEK_END : fine del file
35
36
L’accesso diretto a file − 3
Esempio: dimensione del file
/* Determinazione del numero di caratteri di un file con fseek e ftell */
#include <stdio.h>
La funzione ftell() richiede, come unico argomento, un
puntatore a file e restituisce la posizione corrente
dell’indicatore di posizione nel file
La posizione restituita da ftell() si intende relativa all’inizio
del file…
main(int argc, char **argv)
{
FILE *fp;
long n;
if (argc<2)
printf(“File non specificato\n”);
else
{
fp=fopen(argv[1], “r”);
/* apertura del file */
if (fp != NULL)
{
fseek(fp, 0, SEEK_END); /* puntatore alla fine del file */
n=ftell(fp);
/* lettura posizione del puntatore */
fclose(fp);
printf(“Dimensione del file %ld\n”, n);
}
else
printf(“Errore: il file %s non esiste”, argv[1]);
}
…per flussi binari rappresenta il numero di caratteri dall’inizio
del file alla posizione corrente
…per flussi testuali rappresenta un valore dipendente
dall’implementazione, significativo solo se utilizzato come
parametro per la fseek()
cur_pos = ftell(fp);
if (search(string) == FAIL)
fseek(fp, cur_pos, SEEK_set);
Se la ricerca di una certa stringa nel
file fallisce, l’indicatore di posizione
nel file viene riportato al valore
originale
}
37
Lettura e scrittura di dati
38
La gestione degli errori
Su un file aperto è possibile utilizzare il puntatore a file per
svolgere operazioni di lettura e scrittura
Le operazioni di accesso al file possono essere effettuate su
oggetti di granularità diversa, in particolare a livello di…
Ogni funzione di I/O restituisce un
valore speciale in caso di errore:
alcune restituiscono zero, altre un
valore diverso da zero o EOF
Per ogni flusso aperto, esistono
due campi booleani nella struttura
FILE che registrano condizioni di
errore o di fine file
Si può accedere ai campi di fine
file e di errore utilizzando le
funzioni feof() e ferror()
La funzione clearerr() pone
entrambi i campi a zero
…carattere
…linea
…blocco
Qualsiasi sia la granularità, è impossibile leggere da un flusso
e quindi scrivere sullo stesso flusso senza che fra le due
operazioni venga effettuata una chiamata a fseek(), rewind()
o fflush()
Le funzioni fseek(), rewind() e fflush() sono le uniche
funzioni di I/O che forzano la scrittura del buffer sul flusso
39
#include <stdio.h>
#define ERR_FLAG 1
#define EOF_FLAG 2
char stream_stat(fp)
FILE *fp;
{
/* se nessun campo booleano è alterato, stat vale 0;
* se il solo campo di errore è alterato, stat vale 1,
* se il solo campo di fine file è alterato, stat vale 2
* se sono alterati entrambi, stat vale 3
*/
char stat = 0;
if (ferror(fp))
stat |= ERR_FLAG;
if (feof(fp))
stat |= EOF_FLAG;
clearerr(fp);
return stat;
}
40
L’allocazione dinamica
della memoria − 1
Alle variabili con durata fissa viene riservata memoria per l’intera
durata del programma, mentre alle variabili con durata automatica
la memoria viene allocata ogni volta che si esegue il blocco
relativo: in entrambi i casi si suppone di conoscere la quantità di
memoria da allocare nel momento i cui si scrive il codice sorgente
Tuttavia, talvolta l’occupazione di memoria dipende strettamente
dai dati in ingresso
In C, esistono quattro funzioni della libreria di run−time che
allocazione dinamica della memoria
permettono l’allocazione
malloc() − alloca un numero specificato di byte in memoria e
restituisce un puntatore all’inizio del blocco allocato
calloc() − come malloc()
malloc(), ma inizializza a zero i byte allocati; consente
di allocare la memoria per più di un oggetto alla volta
realloc() − cambia la dimensione di un blocco precedentemente
allocato
free() − libera la memoria che era stata allocata con malloc()
malloc(), calloc()
o realloc()
41