I/O binario continua
[gapil.git] / filestd.tex
index bbc7bc0ddf8fbece27978f3b4b85c4b606719fde..2a22545a15e690ba79c4eeeefedda7601eb4c78e 100644 (file)
@@ -1,4 +1,4 @@
-\chapter{I file: l'interfaccia standard ANSI C}
+ \chapter{I file: l'interfaccia standard ANSI C}
 \label{cha:files_std_interface}
 
 Esamineremo in questo capitolo l'interfaccia standard ANSI C per i file,
@@ -126,7 +126,7 @@ riguarda l'aspetto della scrittura dei dati sul file.
 
 I caratteri che vengono scritti su uno stream normalmente vengono accumulati
 in un buffer e poi trasmessi in blocco in maniera asincrona rispetto alla
-scrittura (quello che viene chiamato lo \textsl{scarico}, dall'ingelese 
+scrittura (quello che viene chiamato lo \textsl{scarico}, dall'inglese 
 \textit{flush}, dei dati) tutte le volte che il buffer viene riempito. Un
 comportamento analogo avviene anche in lettura (cioè dal file viene letto un
 blocco di dati, anche se se ne sono richiesti una quantità inferiore), ma la
@@ -271,33 +271,35 @@ funzioni delle librerie standard del C.
   \label{tab:file_fopen_mode}
 \end{table}
 
-In realtà lo standard ANSI C prevede un totale di 15 possibili valori diversi
-per \param{mode}, ma in \tabref{tab:file_fopen_mode} si sono riportati solo i
-sei valori effettivi, ad essi può essere aggiunto pure il carattere \func{b}
-(come ultimo carattere o nel mezzo agli altri per le stringhe di due
-caratteri) che in altri sistemi operativi serve a distinguere i file binari
-dai file di testo; in un sistema POSIX questa distinzione non esiste e il
-valore viene accettato solo per compatibilità, ma non ha alcun effetto.
-
-Inoltre nel caso si usi \func{fdopen} i valori specificati da \param{mode}
-devono essere compatibili con quelli con cui il file descriptor è stato
-aperto. Inoltre i modi \cmd{w} e \cmd{w+} non troncano il file. La posizione
-nello stream viene settata a quella corrente nel file descriptor, e le
-variabili di errore e di fine del file sono cancellate. Il file non viene
-duplicato e verrà chiuso alla chiusura dello stream.
+In realtà lo standard ANSI C prevede un totale di 15 possibili valori
+diversi per \param{mode}, ma in \tabref{tab:file_fopen_mode} si sono
+riportati solo i sei valori effettivi, ad essi può essere aggiunto pure
+il carattere \func{b} (come ultimo carattere o nel mezzo agli altri per
+le stringhe di due caratteri) che in altri sistemi operativi serve a
+distinguere i file binari dai file di testo; in un sistema POSIX questa
+distinzione non esiste e il valore viene accettato solo per
+compatibilità, ma non ha alcun effetto.
+
+Inoltre nel caso si usi \func{fdopen} i valori specificati da
+\param{mode} devono essere compatibili con quelli con cui il file
+descriptor è stato aperto. Inoltre i modi \cmd{w} e \cmd{w+} non
+troncano il file. La posizione nello stream viene settata a quella
+corrente nel file descriptor, e le variabili di errore e di fine del
+file sono cancellate. Il file non viene duplicato e verrà chiuso alla
+chiusura dello stream.
 
 I nuovi file saranno creati secondo quanto visto in
-\secref{sec:file_ownership} ed avranno i permessi di accesso settati al valore
-\macro{S\_IRUSR|S\_IWUSR|S\_IRGRP|S\_IWGRP|S\_IROTH|S\_IWOTH} (pari a
-\macro{0666}) modificato secondo il valore di \acr{umask} per il processo (si
-veda \secref{sec:file_umask}).
-
-In caso di file aperti in lettura e scrittura occorre ricordarsi che c'è di
-messo una bufferizzazione; per questo motivo lo standard ANSI C richiede che
-ci sia una operazione di posizionamento fra una operazione di output ed una di
-input o viceversa (eccetto il caso in cui l'input ha incontrato la fine del
-file), altrimenti una lettura può ritornare anche il risultato di scritture
-precedenti l'ultima effettuata.
+\secref{sec:file_ownership} ed avranno i permessi di accesso settati al
+valore \macro{S\_IRUSR|S\_IWUSR|S\_IRGRP|S\_IWGRP|S\_IROTH|S\_IWOTH}
+(pari a \macro{0666}) modificato secondo il valore di \acr{umask} per il
+processo (si veda \secref{sec:file_umask}).
+
+In caso di file aperti in lettura e scrittura occorre ricordarsi che c'è
+di messo una bufferizzazione; per questo motivo lo standard ANSI C
+richiede che ci sia una operazione di posizionamento fra una operazione
+di output ed una di input o viceversa (eccetto il caso in cui l'input ha
+incontrato la fine del file), altrimenti una lettura può ritornare anche
+il risultato di scritture precedenti l'ultima effettuata.
 
 Per questo motivo è una buona pratica (e talvolta necessario) far seguire ad
 una scrittura una delle funzioni \func{fflush}, \func{fseek}, \func{fsetpos} o
@@ -325,7 +327,7 @@ La funzione effettua uno scarico di tutti i dati presenti nei buffer di uscita
 e scarta tutti i dati in ingresso, se era stato allocato un buffer per lo
 stream questo verrà rilasciato. La funzione effettua lo scarico solo per i
 dati presenti nei buffer in user space usati dalle \acr{glibc}; se si essere
-sicuri che il kernel forzi la scrittura su disco occorrà effettuare . 
+sicuri che il kernel forzi la scrittura su disco occorrerà effettuare . 
 
 Linux supporta, come estensione implementata dalle \acr{glibc}, anche una
 altra funzione, \func{fcloseall}, che serve a chiudere tutti i file, il suo
@@ -346,14 +348,136 @@ chiuso i file e prima di uscire (si ricordi quanto visto in
 \subsection{Lettura e scrittura su uno stream}
 \label{sec:file_io}
 
+Una delle caratteristiche più utili dell'interfaccia degli stream è la
+ricchezza delle funzioni disponibili per le operazioni di lettura e
+scrittura sui file. Sono infatti previste ben tre diverse modalità
+modalità di input/output non formattato:
+\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}.
+\end{enumerate}
+ed inoltre la modalità di input/output formattato.
+
+A differenza dell'interfaccia dei file descriptor il raggiungimento della fine
+del file è considerato un errore, e viene notificato come tale dai valori di
+uscita delle varie funzioni; nella maggior parte dei casi questo avviene con
+la restituzione del valore intero (di tipo \type{int}) \macro{EOF}\footnote{la
+  costante deve essere negativa, le \acr{glibc} usano -1, altre
+  implementazioni possono avere valori diversi.}  definito anch'esso
+nell'header \func{stdlib.h}.
+
+Dato che le funzioni dell'interfaccia degli stream sono funzioni di libreria
+che si appoggiano a delle system call, esse non settano direttamente la
+variabile \var{errno}, che mantiene il valore settato dalla system call che ha
+riportato l'errore. 
+
+Siccome la condizione di end-of-file è anch'essa segnalata come errore, nasce
+il problema di come distinguerla da un errore effettivo; basarsi solo sul
+valore di ritorno della funzione e controllare il valore di \var{errno}
+infatti non basta, dato che quest'ultimo potrebbe essere stato settato in una
+altra occasione, (si veda \secref{sec:sys_errno} per i dettagli del
+funzionamento di \var{errno}).
+
+Per questo motivo tutte le implementazioni delle librerie standard
+mantengono per ogni stream almeno due flag all'interno dell'oggetto
+\type{FILE}, il flag di \textit{end-of-file}, che segnala che si è
+raggiunta la fine del file in lettura, e quello di errore, che segnala
+la presenza di un qualche errore nelle operazioni di input/output;
+questi flag possono essere riletti dalle funzioni:
+\begin{functions}
+  \headdecl{stdio.h}
+  \funcdecl{int feof(FILE *stream)}
+  Controlla il flag di end-of-file di \param{stream}.
+  \funcdecl{int ferror(FILE *stream)}
+  Controlla il flag di errore di \param{stream}.
 
-\subsection{Posizionamento su uno stream}
-\label{sec:file_fseek}
+  Entrambe le funzioni ritornano un valore diverso da zero se i relativi
+  flag sono settati. 
+\end{functions}
+\noindent si tenga presente comunque che la lettura di questi flag segnala
+soltanto che c'è stato un errore, o che si è raggiunta la fine del file in una
+qualunque operazione sullo stream, il controllo quindi deve essere effettuato
+ogni volta che si chiama una funzione di libreria.
+
+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)}
+  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
+corretta la causa di un errore per evitare di mantenere i flag attivi,
+così da poter rilevare una successiva ulteriore condizione di errore.
 
 
 \subsection{Input/output binario}
 \label{sec:file_binary_io}
 
+La prima modalità di input/output non formattato ricalca quella della
+intefaccia dei file descriptor, e provvede semplicemente la scrittura e
+la lettura dei dati da un buffer verso un file e vicecersa. In generale
+questa è la modalità che si usa quando si ha a che fare con dati non
+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}.
+  
+  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 leggere o scrivere
+un array o una struttura; un tipico esempio è quello in cui si salva una
+parte di un vettore con una chiamata del tipo:
+\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
+int WriteVect(FILE * stream, double * vec, size_t nelem) 
+{
+    int size, nread;
+    size = sizeof(*vec);
+    if ( (nread = fwrite(vec, size, nelem, stream)) != nelem) {
+        perror("Write error");
+    }
+    return nread;
+}
+\end{lstlisting}
+nel qual caso devono essere specificate dimensione e numero degli
+elementi del vettore; nel caso di una struttura invece si avrà un
+qualcosa tipo:
+\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
+struct histogram {
+    int nbins; 
+    double max, min;
+    double * bin;
+} histo; 
+int WriteStruct(FILE * stream, struct histogram * histo, size_t nelem) 
+{
+    if ( fwrite(vec, sizeof(*histo), 1, stream) !=1) {
+        perror("Write error");
+    }
+    return nread;
+}
+\end{lstlisting}
+
+
+
+
+\subsection{Input/output a caratteri singoli}
+\label{sec:file_char_io}
+
+
+
 
 \subsection{Input/output di linea}
 \label{sec:file_line_io}
@@ -363,6 +487,11 @@ chiuso i file e prima di uscire (si ricordi quanto visto in
 \label{sec:file_formatted_io}
 
 
+\subsection{Posizionamento su uno stream}
+\label{sec:file_fseek}
+
+
+
 \section{Funzioni avanzate}
 \label{sec:file_stream_adv_func}
 
@@ -386,13 +515,39 @@ operazioni vengono effettuate un una subroutine, che a questo punto
 necessiterà di informazioni aggiuntive rispetto al semplice puntatore allo
 stream; questo può essere evitato con le due funzioni \func{\_\_freadable} e
 \func{\_\_fwritable} i cui prototipi sono:
-
 \begin{functions}
   \headdecl{stdio\_ext.h}
   \funcdecl{int \_\_freadable (FILE * stream)}
-  \funcdecl{int \_\_fwritable(FILE * stream)}
+  Restituisce un valore diverso da zero se \param{stream} consente la lettura.
+
+  \funcdecl{int \_\_fwritable(FILE * stream)}  
+  Restituisce un valore diverso da zero se \param{stream} consente la
+  scrittura.
+\end{functions}
+
+Altre due funzioni, \func{\_\_freading} e \func{\_\_fwriting} servono ad un
+uso ancora più specialistico, il loro prototipo è: 
+\begin{functions}
+  \headdecl{stdio\_ext.h}
+  \funcdecl{int \_\_freading (FILE * stream)}
+  Restituisce un valore diverso da zero se \param{stream} è aperto in sola
+  lettura o se l'ultima operazione è stata di lettura.
+
+  \funcdecl{int \_\_fwriting(FILE * stream)}  
+  Restituisce un valore diverso da zero se \param{stream} è aperto in sola
+  scrittura o se l'ultima operazione è stata di scrittura.
 \end{functions}
 
+Le due funzioni hanno lo scopo di determinare di che tipo è stata l'ultima
+operazione eseguita su uno stream aperto in lettura/scrittura; ovviamente se
+uno stream è aperto in sola lettura (o sola scrittura) la modalità dell'ultima
+operazione è sempre determinata; l'unica ambiguità è quando non sono state
+ancora eseguite operazioni, in questo caso le funzioni rispondono come se
+una operazione ci fosse comunque stata.
+
+La conoscenza dell'ultima operazione effettuata su uno stream aperto in
+lettura/scrittura è utile in quanto permette di trarre conclusioni sullo stato
+del buffer e del suo contenuto.
 
 
 \subsection{Il controllo della bufferizzazione}