Dal punto di vista del linguaggio C un file è una sequenza di
byte
senza alcuna strutturazione. Il C fornisce, tramite la libreria del
linguaggio, una grande varietà di istruzioni di input/output
(brevemente I/O). Nello standard ANSI sono presenti
una quarantina di diverse istruzioni di I/O. In genere, l' I/O C
avviene
attraverso un buffer,
un' area di memoria intermedia che funge da contenitore
temporaneo
dei byte in attesa che questi vengano letti (in input) o scritti
(in output). La presenza del buffer come contenitore temporaneo dei
dati permette di gestire meglio, in modo asincrono, le lente
operazioni di I/O da parte del sistema operativo, migliorando l'
efficienza complessiva dell' elaborazione. Tuttavia, in caso di
necessità, il programmatore può intervenire
sulla dimensione e sullo svuotamento del buffer.
Lo standard ha inoltre introdotto la distinzione tra files binari
e di testo, a volte ridondante (p.es. sui sistemi UNIX), ma che aumenta
la
portabilità dei
programmi.
Il principale concetto dell' I/O del C è che questo avviene mediante facendo riferimento non a file ma a , flussi di dati (stream), associati a files o devices (periferiche). Uno stream consiste in una sequenza ordinata di bytes. Ogni operazione di lettura o scrittura su file corrisponde a leggere o scrivere dati dallo o sullo stream associato a quel file.
La prima operazione necessaria per ogni operazione di
lettura/scrittura è dunque associare uno stream ad un file o ad
una
periferica di I/O.
Nell' I/O con buffer questa associazione è fatta tramite
pointer a
struttura di tipo FILE.
FILE è una struttura, definita in stdio.h, contenente diversi
campi
per informazioni quali : nome del file, modo di accesso,
puntatori
al prossimo carattere dello stream. Tuttavia, dal punto di vista del
programmatore non è necessario conoscere i dettagli interni
della
struttura FILE.
Ci sono sempre 3 streams connessi permanentemente con un processo: stdin, stdout, stderr.
Usualmente, in un' interazione diretta col computer, stdin
punta alla tastiera, mentre stdout e stderr allo schermo. E'
però
possibile ridefinire questi simboli associandoli per esempio a file su
disco.
Function fopen("nomefile","modo"). "nomefile" e' una stringa
che rappresenta il nome del nuovo file, mentre "modo" puo' essere:
"r" | Apre un file esistente in lettura |
"a" | Apre un file esistente in modo "append". La scrittura e' sempre alla posizione dell end of file |
"w" | Crea un nuovo file in scrittura |
"r+" | lettura e scrittura |
"w+" | scrittura e lettura |
"a+" | scrittura in modo append e lettura |
Inoltre, per ciascuno di questo modi, puo' essere
specificato che va applicato a file "binari" aggiungendo
una b dopo la codifica del modo. Su sistemi UNIX non c' e' differenza
tra file di testo e binari dal punto di vista della
function fopen.
Con i modi "r" e r+" il file deve esistere.
Con i modi "w" e w+" il file viene creato se non esiste ma ne viene cancellato il contenuto se già esiste e non è vuoto.
Un file aperto può essere chiuso col comando fclose(fp)
che
chiude il file associato al puntatore fp. La chiusura del file libera
la
struttura FILE a cui fp punta. Inoltre provoca lo svuotamento di ogni
buffer associato allo stream concludendone le operazioni di I/O. Tutti
i files di un programma che termina normalmente sono automaticamente
chiusi.
Le operazioni di I/O possono essere basate su tre diversi livelli: a
carattere, a linea e a blocco.
Le istruzioni di lettura/scrittura a carattere sono:
int getc(FILE *stream) getchar(void) |
legge un carattere dallo stream (usualmente una macro) ritornandolo come int o EOF se raggiunge la fine del file. getchar legge da stdin |
int putc(int c, FILE *stream) | scrive un carattere (usualmente una macro). Ritorna il carattere scrittto o EOF se si è verificata una condizione di errore. |
int fgetc(FILE *stream) | legge un carattere dallo stream (function) |
int fputc(int c, FILE *stream) | scrive un carattere (function) |
Lettura e scrittura a righe:
char *fgets(char *s, int size, FILE *stream),
char *gets(char *s) |
legge una sequenza di caratteri dallo
stream fino al carattere newline (\n) o ad un massimo numero (size) di
caratteri e lo assegna ad una stringa. gets opera come fgets ma legge stdin, non include il carattere newline sulla stringa e non permette di leggere un numero predefinito di caratteri. A causa dei problemi legati all' assenza di controllo sul numero dei caratteri letti il suo uso è sconsigliato. |
int fputs(const char *s, FILE *stream), int puts(const char *s) |
scrive una stringa sullo stream senza il terminatore di stringa (\0) Ritorna EOF in caso di errore |
Mediante queste funzioni primitive di lettura/scrittura si possono
ottenere istruzioni di I/O piu' complesse aggiungendo eventuali
conversioni di sequenze di caratteri nelle rappresentazioni interne dei
vari tipi dati. Alcune di queste possibilita' di I/O e conversione sono
svolte da alcune function di libreria:
int scanf(const char *format, ...) | legge e converte in accordo ai convertitori di formato presenti nella stringa format le variabili da stdin. Ritorna il numero delle variabili assegnate con successo. |
int fscanf(FILE *stream, const char *format, ...) | legge e converte in accordi ai convertitori di formato le variabili presenti sullo stream indicato come primo argomento |
int sscanf(const char *str, const char *format, ...) | legge e converte in accordi ai convertitori di formato le variabili presenti sulla stringa str |
int printf(const char *format, ...) | converte e scrive su stdout in accordo ai convertitori di formato le variabili nella lista (se presenti) nelle posizioni caratterizzate dei convertitori di formato nella stringa il valore intero di ritorno e' il numero di caratteri stampati |
int fprintf(FILE *stream, const char *format, ...) | come printf ma su file |
int sprintf(char *str, const char *format, ...) | come printf ma su stringa |
int snprintf(char *str, size_t size, const char *format,...) | come sprintf ma scrive al piu' size caratteri (incluso il terminatore di stringa \0) |
La stringa di formato contiene caratteri e convertitori
di formato. Nella printf e affini, i caratteri sono
interpretati come costanti letterali che vanno scritte in output.
Nella scanf e affini, i caratteri <spazio> e <tab> sono
interpretati come "qualsiasi sequenza di uno o piu' separatori
tra dati (a meno che non ci sia un convertitore a carattere, nel qual
caso uno spazio viene interpretato come il carattere corrispondente.
Altri caratteri indicano costanti letterali che devono essere presenti
nello stream di input, pena un errore in lettura.
Esempio: l' istruzione scanf("Numero : %d Giorno : %s",x,giorno); permette di leggere la seguente linea di input:
Numero : 13 Giorno : venerdi'
assegnando alle variabili x e giorno i valori 13 e "venerdi'". Se
tuttavia un campo di input fosse diverso anche per un solo
carattere dalla sequanza di caratteri nel format, la lettura
terminerebbe in corrispondenza di quel campo.
Lettura e scrittura a blocchi.
size_t
fread ((void *) restrict ptr, size_t size, size_t nmemb, FILE * restrict stream) |
Legge,
in un blocco che inizia alla posizione indicata da ptr, nmemb dati di dimensione size
dallo stream. Ritorna il numero di dati letti realmente |
size_t
fwrite (const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream) |
Scrive
i dati nel blocco che inizia alla posizione indicata da ptr in uno
stream |
Alcune functions della libreria C permettono di gestire e controllare
l' I/O. Per la spiegazione degli argomenti e del modo di uso,
fare riferimento alle corrispondenti man
page.
int
setvbuf(FILE *stream, char *buf, int mode , size_t size) |
permette di modificare il tipo
(senza b., orientato al blocco o alla linea), la dimensione e la
localizzazione del buffer |
int ungetc(int c,FILE *stream) | rimette il carattere c sullo stream. Una successiva lettura di un carattere ritornerebbe c. L' indicatore di posizione sul file viene decrementato di uno. |
int fflush(FILE *stream) | forza la scrittura di tutti i dati del buffer relativo allo stream di output stream. Ritorna 0 se ha successo,EOF altrimenti. Non andrebbe usata su stream di input in quanto il risultato non è univocamente determinato dallo standard. |
int fseek( FILE *stream, long offset, int whence) | modifica l' indicatore di posizione per lo stream in argomento aggiungendo offset alla posizione indicata con whence |
long ftell( FILE *stream) | ottiene il valore corrente dell' indicatore di posizione per lo stream in argomento |
void rewind( FILE *stream) | riporta all' inizio l' indicatore di posizione dello stream in argomento |
int feof( FILE *stream) | verifica la condizione di end of file per lo stream in argomento |
void clearerr( FILE *stream) | azzera la condizione di errore o di end of file per lo stream in argomento |