\section{Introduzione}
\label{sec:file_stream_intro}
-Come visto in \capref{cap:file_unix_interface} le operazioni di I/O sui file
+Come visto in \capref{cha:file_unix_interface} le operazioni di I/O sui file
sono gestibili a basso livello con l'interfaccia standard unix, che ricorre
direttamente alle system call messe a disposizione dal kernel.
\subsection{Apertura e chiusura di uno stream}
\label{sec:file_fopen}
-Le funzioni che permettono di aprire uno stream sono tre\footnote{\func{fopen}
- e \func{freopen} fanno parte dello standard ANSI C, \func{fdopen} è parte
-dello standard POSIX.1.}, i loro prototipi sono:
+Le funzioni che si possono usare per aprire uno stream sono solo
+tre\footnote{\func{fopen} e \func{freopen} fanno parte dello standard
+ ANSI C, \func{fdopen} è parte dello standard POSIX.1.}, i loro
+prototipi sono:
\begin{functions}
\headdecl{stdio.h}
\funcdecl{FILE * fopen(const char * path, const char *mode)}
stream)}
Apre il file specificato da \param{path} associandolo allo stream
specificato da \param{stream}, se questo è già aperto prima lo chiude.
-
- Le funzioni ritornano un puntatore valido in caso di successo e \macro{NULL}
- in caso di errore, in tal caso \var{errno} viene settata al valore ricevuto
- dalla funzione sottostante di cui è fallita l'esecuzione.
- Gli errori pertanto possono essere quelli di \func{malloc} per tutte e tre
- le funzioni, quelli \func{open} per \func{fopen}, quelli di \func{fcntl} per
- \func{fdopen} e quelli di \func{fopen}, \func{fclose} e \func{fflush} per
- \func{freopen}.
+ \bodydesc{Le funzioni ritornano un puntatore valido in caso di
+ successo e \macro{NULL} in caso di errore, in tal caso \var{errno}
+ viene settata al valore ricevuto dalla funzione sottostante di cui è
+ fallita l'esecuzione.
+
+ Gli errori pertanto possono essere quelli di \func{malloc} per tutte
+ e tre le funzioni, quelli \func{open} per \func{fopen}, quelli di
+ \func{fcntl} per \func{fdopen} e quelli di \func{fopen},
+ \func{fclose} e \func{fflush} per \func{freopen}.}
\end{functions}
Normalmente la funzione che si usa per aprire uno stream è \func{fopen},
\begin{prototype}{stdio.h}{int fclose(FILE * stream)}
Chiude lo stream \param{stream}.
- Restituisce 0 in caso di successo e \macro{EOF} in caso di errore, nel qual
- caso setta \var{errno} a \macro{EBADF} se il file descriptor indicato da
- \param{stream} non è valido, o uno dei valori specificati dalla sottostante
- funzione che è fallita (\func{close}, \func{write} o \func{fflush}).
+ \bodydesc{Restituisce 0 in caso di successo e \macro{EOF} in caso di errore,
+ nel qual caso setta \var{errno} a \macro{EBADF} se il file descriptor
+ indicato da \param{stream} non è valido, o uno dei valori specificati
+ dalla sottostante funzione che è fallita (\func{close}, \func{write} o
+ \func{fflush}).}
\end{prototype}
La funzione effettua uno scarico di tutti i dati presenti nei buffer di
\begin{prototype}{stdio.h}{int fcloseall(void)}
Chiude tutti gli stream.
- Restituisce 0 se non ci sono errori ed \macro{EOF} altrimenti.
+ \bodydesc{Restituisce 0 se non ci sono errori ed \macro{EOF} altrimenti.}
\end{prototype}
la funzione esegue lo scarico dei dati bufferizzati in uscita e scarta quelli
in ingresso, chiudendo tutti i file. Questa funzione è provvista solo per i
\begin{enumerate*}
\item\textsl{binario} in cui legge/scrive un blocco di dati alla
volta, vedi \secref{sec:file_binary_io}.
-\item\textsl{di linea} in cui si legge/scrive una linea (terminata dal
- carattere di newline \verb|\n|) alla volta, vedi
- \secref{sec:file_line_io}.
\item\textsl{a caratteri} in cui si legge/scrive un carattere alla
volta (con la bufferizzazione gestita automaticamente dalla libreria),
vedi \secref{sec:file_char_io}.
+\item\textsl{di linea} in cui si legge/scrive una linea (terminata dal
+ carattere di newline \verb|\n|) alla volta, vedi
+ \secref{sec:file_line_io}.
\end{enumerate*}
ed inoltre la modalità di input/output formattato.
Entrambi i flag (di errore e di end-of-file) possono essere cancellati usando
la funzione \func{clearerr}, il cui prototipo è:
-\begin{prototype}{stdio.h}{void clearerr( FILE *stream)}
+\begin{prototype}{stdio.h}{void clearerr(FILE *stream)}
Cancella i flag di errore ed end-of-file di \param{stream}.
\end{prototype}
\noindent in genere si usa questa funziona una volta che si sia identificata e
formattati. Le due funzioni che si usano per l'I/O binario sono:
\begin{functions}
\headdecl{stdio.h}
- \funcdecl{size\_t fread(void * ptr, size\_t size,
- size\_t nmemb, FILE * stream)}
- \funcdecl{size\_t fwrite(const void *
- ptr, size\_t size, size\_t nmemb, FILE * stream)}
- Le funzioni rispettivamente leggono e scrivono \param{nmemb} elementi
- di dimensione \param{size} dal buffer \param{ptr} al file \param{stream}.
+ \funcdecl{size\_t fread(void * ptr, size\_t size, size\_t nmemb, FILE
+ * stream)}
+
+ \funcdecl{size\_t fwrite(const void * ptr, size\_t size, size\_t
+ nmemb, FILE * stream)}
+
+ Rispettivamente leggono e scrivono \param{nmemb} elementi di dimensione
+ \param{size} dal buffer \param{ptr} al file \param{stream}.
- Entrambe le funzioni ritornano il numero di elementi letti o scritti,
- in caso di errore o fine del file viene restituito un numero di
- elementi inferiore al richiesto.
+ \bodydesc{Entrambe le funzioni ritornano il numero di elementi letti o
+ scritti, in caso di errore o fine del file viene restituito un numero di
+ elementi inferiore al richiesto.}
\end{functions}
In genere si usano queste funzioni quando si devono trasferire su file
\funcdecl{size\_t fwrite\_unlocked(const void * ptr, size\_t size,
size\_t nmemb, FILE * stream)}
- Le funzioni sono identiche alle analoghe \func{fread} e \func{fwrite}
- ma non acquisiscono il lock implicito sullo stream.
+ \bodydesc{Le funzioni sono identiche alle analoghe \func{fread} e
+ \func{fwrite} ma non acquisiscono il lock implicito sullo stream.}
\end{functions}
\noindent entrambe le funzioni sono estensioni GNU previste solo dalle
- \acr{glibc}.
+\acr{glibc}.
-\subsection{Input/output a caratteri singoli}
+\subsection{Input/output a caratteri}
\label{sec:file_char_io}
+La seconda modalità di input/output è quella a caratteri, in cui si
+trasferisce un carattere alla volta. Le funzioni per la lettura a
+caratteri sono tre, \func{fgetc}, \func{getc} e \func{getchar}, i
+rispettivi prototipi sono:
+\begin{functions}
+ \headdecl{stdio.h}
+
+ \funcdecl{int getc(FILE *stream)} Legge un byte da \param{stream} e lo
+ restituisce come intero. In genere è implementata come una macro.
+ \funcdecl{int fgetc(FILE *stream)} Legge un byte da \param{stream} e lo
+ restituisce come intero. È una sempre una funzione.
+
+ \funcdecl{int getchar(void)} Equivalente a \func{getc(stdin)}.
+
+ \bodydesc{Tutte queste funzioni leggono un byte alla volta, che viene
+ restituito come intero; in caso di errore o fine del file il valore
+ di ritorno è \macro{EOF}.}
+\end{functions}
+
+A parte \func{getchar}, che si usa in genere per leggere un carattere da
+tastiera, le altre due funzioni sono sostanzialmente equivalenti. La
+differenza è che \func{gets} è ottimizzata al massimo e normalmente
+viene implementata con una macro, per cui occorre stare attenti a cosa
+le si passa come argomento, infatti \param{stream} può essere valutato
+più volte nell'esecuzione, e non viene passato in copia con il
+meccanismo visto in \secref{sec:proc_var_passing}; per questo motivo se
+si passa una espressione si possono avere effetti indesiderati.
+
+Invece \func{fgets} è assicurata essere sempre una funzione, per questo
+motivo la sua esecuzione normalmente è più lenta per via dell'overhead
+della chiamata, ma è altresì possibile ricavarne l'indirizzo, che può
+essere passato come parametro ad un altra funzione (e non si hanno i
+problemi accennati in precedenza con \param{stream}).
+
+Le tre funzioni restituiscono tutte un \type{unsigned char} convertito
+ad \type{int} (si usa \type{unsigned char} in modo da evitare
+l'espansione del segno). In questo modo il valore di ritorno è sempre
+positivo, tranne in caso di errore o fine del file.
+
+Nelle estensioni GNU che provvedono la localizzazione sono definite tre
+funzioni equivalenti alle precedenti che invece di un carattere di un
+byte restituiscono un carattere in formato esteso (cioè di tipo
+\type{wint\_t}, il loro prototipo è:
+\begin{functions}
+ \headdecl{stdio.h}
+
+ \funcdecl{wint\_t getwc(FILE *stream)} Legge un carattere esteso da
+ \param{stream}. In genere è implementata come una macro.
+
+ \funcdecl{wint\_t fgetwc(FILE *stream)} Legge un carattere esteso da
+ \param{stream} È una sempre una funzione.
+
+ \funcdecl{wint\_t getwchar(void)} Equivalente a \func{getwc(stdin)}.
+
+ \bodydesc{Tutte queste funzioni leggono un carattere alla volta, in
+ caso di errore o fine del file il valore di ritorno è \macro{WEOF}.}
+\end{functions}
+
+Per scrivere un carattere si possono usare tre funzioni analoghe alle
+precedenti usate per leggere: \func{putc}, \func{fputc} e
+\func{putchar}; i loro prototipi sono:
+\begin{functions}
+ \headdecl{stdio.h}
+
+ \funcdecl{int putc(int c, FILE *stream)} Scrive il carattere \param{c}
+ su \param{stream}. In genere è implementata come una macro.
+
+ \funcdecl{int fputc(FILE *stream)} Scrive il carattere \param{c} su
+ \param{stream}. È una sempre una funzione.
+
+ \funcdecl{int putchar(void)} Equivalente a \func{putc(stdin)}.
+
+ \bodydesc{Le funzioni scrivono sempre un carattere alla volta, il cui
+ valore viene restituito in caso di successo; in caso di errore o
+ fine del file il valore di ritorno è \macro{EOF}.}
+\end{functions}
+
+Tutte queste funzioni scrivono sempre un byte alla volta, anche se
+prendono come parametro un \type{int} (che pertanto deve essere ottenuto
+con un cast da un \type{unsigned char}). Anche il valore di ritorno è
+sempre un intero; in caso di errore o fine del file il valore di ritorno
+è \macro{EOF}.
+
+Come nel caso dell'I/O binario le \acr{glibc} provvedono per ciascuna
+delle funzioni precedenti una seconda funzione, il cui nome è ottenuto
+aggiungendo un \func{\_unlocked}, che esegue esattamente le stesse
+operazioni evitando però il lock implicito dello stream.
+
+Per compatibilità con SVID sono provviste anche due funzioni per leggere
+e scrivere una \textit{word} (che è sempre definita come \type{int}); i
+loro prototipi sono:
+\begin{functions}
+ \headdecl{stdio.h}
+
+ \funcdecl{int getw(FILE *stream)} Legge una parola da \param{stream}.
+ \funcdecl{int putw(int w, FILE *stream)} Scrive la parola \param{w} su
+ \param{stream}.
+
+ \bodydesc{Le funzioni restituiscono la parola \param{w}, o \macro{EOF}
+ in caso di errore o di fine del file.}
+\end{functions}
+\noindent l'uso di queste funzioni è deprecato in favore dell'uso di
+\func{fread} e \func{fwrite}, in quanto non è possibile distinguere il
+valore -1 da una condizione di errore che restituisce \macro{EOF}.
+
+Una degli usi più frequenti dell'input/output a caratteri è nei
+programmi di \textit{parsing} in cui si analizza il testo; in questo
+contesto diventa utile poter analizzare il carattere successivo da uno
+stream senza estrarlo effettivamente (la tecnica è detta \textit{peeking
+ ahead}) in modo che il programma possa regolarsi sulla base avendo
+dato una \textsl{sbirciatina} a quello che viene dopo.
+
+Nel nostro caso questo tipo di comportamento può essere realizzato prima
+leggendo il carattere, e poi rimandandolo indietro, cosicché ridiventi
+disponibile per una lettura successiva; la funzione che inverte la
+lettura si chiama \func{ungetc} ed il suo prototipo è:
+\begin{prototype}{stdio.h}{int ungetc(int c, FILE *stream)}
+ Rimanda indietro il carattere \param{c}, con un cast a \type{unsigned
+ char}, sullo stream \param{stream}.
+
+ \bodydesc{La funzione ritorna \param{c} in caso di successo e
+ \macro{EOF} in caso di errore.}
+\end{prototype}
+benché lo standard ANSI C preveda che l'operazione possa essere ripetuta
+per un numero arbitrario di caratteri, alle implementazioni è richiesto
+di garantire solo un livello; questo è quello che fa la \acr{glibc}, che
+richiede che avvenga un'altra operazione fra due \func{ungetc}
+successive.
+
+Non è necessario che il carattere che si manda indietro sia l'ultimo che
+si è letto, e non è necessario neanche avere letto nessun carattere
+prima di usare \func{ungetc}, ma di norma la funzione è intesa per
+essere usata per rimandare indietro l'ultimo carattere letto.
+
+Nel caso \param{c} sia un \macro{EOF} la funzione non fa nulla, e
+restituisce sempre \macro{EOF}; così si può usare \func{ungetc} anche
+con il risultato di una lettura alla fine del file.
+
+Se si è alla fine del file si può comunque rimandare indietro un
+carattere, il flag di end-of-file verrà automaticamente cancellato
+perché c'è un nuovo carattere disponibile che potrà essere riletto
+successivamente.
+
+Infine si tenga presente che \func{ungetc} non altera il contenuto del
+file, ma opera esclusivamente sul buffer interno. Se si esegue una
+qualunque delle operazioni di riposizionamento (vedi
+\secref{sec:file_fseek}) i caratteri rimandati indietro vengono
+scartati.
\subsection{Input/output di linea}
\label{sec:file_line_io}
+La terza ed ultima modalità di input/output non formattato è quello di
+linea, in cui legge o scrive una riga alla volta; questa è una modalità
+molto usata per l'I/O da terminale, ma che presenta le caratteristiche
+più controverse.
+
+Le funzioni per leggere una linea sono sostanzialmente due, \func{gets} e
+\func{fgets}, i cui rispettivi prototipi sono:
+\begin{functions}
+ \headdecl{stdio.h}
+
+ \funcdecl{char * gets(char * string)} Scrive su \param{string} una
+ linea letta da \var{stdin}.
+
+ \funcdecl{char * fgets(char * string, int size, FILE *stream)}
+ Scrive su \param{string} la linea letta da \param{stream} per un
+ massimo di \param{size} byte.
+
+ \bodydesc{Le funzioni restituiscono della stringa letta o \macro{NULL}
+ in caso di errore.}
+\end{functions}
+
+
\subsection{Input/output formattato}
\label{sec:file_formatted_io}
Gli stream possono essere usati in applicazioni multi-thread allo stesso
modo in cui sono usati nelle applicazioni normali, ma si deve essere
-consapovoli delle possibili complicazioni anche quando non si usano i
+consapevoli delle possibili complicazioni anche quando non si usano i
thread, dato che l'implementazione delle librerie è influenzata
pesantemente dalle richieste necessarie per garantirne l'uso coi thread.