Si prosegue con l'I/O binario
[gapil.git] / filestd.tex
index f93b9e03f3c5ed2d91629cebdd0a846ebd78f73a..a20d207aea09d97e4b69dfb9fd73ef0715b80d83 100644 (file)
@@ -83,81 +83,87 @@ file \file{stdio.h}.
 \subsection{Gli stream standard}
 \label{sec:file_std_stream}
 
-Ai tre file descriptor standard (vedi \secref{sec:file_std_descr}) aperti per
-ogni processo, corrispondono altrettanti stream, che rappresentano i canali
-standard di input/output prestabiliti; anche questi tre stream sono
-identificabili attraverso dei nomi simbolici definiti nell'header
-\file{stdio.h} che sono:
+Ai tre file descriptor standard (vedi \secref{sec:file_std_descr})
+aperti per ogni processo, corrispondono altrettanti stream, che
+rappresentano i canali standard di input/output prestabiliti; anche
+questi tre stream sono identificabili attraverso dei nomi simbolici
+definiti nell'header \file{stdio.h} che sono:
 
 \begin{itemize}
-\item \var{FILE * stdin} Lo \textit{standard input} cioè lo stream da cui
-  il processo riceve ordinariamente i dati in ingresso. Normalmente è associato
-  dalla shell all'input del terminale e prende i caratteri dalla tastiera.
-\item \var{FILE * stdout} Lo \textit{standard input} cioè lo stream su cui
-  il processo invia ordinariamente i dati in uscita. Normalmente è associato
-  dalla shell all'output del terminale e scrive sullo schermo.
-\item \var{FILE * stderr} Lo \textit{standard input} cioè lo stream su cui
-  il processo è supposto inviare i messaggi di errore. Normalmente anch'esso
-  è associato dalla shell all'output del terminale e scrive sullo schermo.
+\item \var{FILE * stdin} Lo \textit{standard input} cioè lo stream da
+  cui il processo riceve ordinariamente i dati in ingresso. Normalmente
+  è associato dalla shell all'input del terminale e prende i caratteri
+  dalla tastiera.
+\item \var{FILE * stdout} Lo \textit{standard input} cioè lo stream su
+  cui il processo invia ordinariamente i dati in uscita. Normalmente è
+  associato dalla shell all'output del terminale e scrive sullo schermo.
+\item \var{FILE * stderr} Lo \textit{standard input} cioè lo stream su
+  cui il processo è supposto inviare i messaggi di errore. Normalmente
+  anch'esso è associato dalla shell all'output del terminale e scrive
+  sullo schermo.
 \end{itemize}
 
-Nelle \acr{glibc} \var{stdin}, \var{stdout} e \var{stderr} sono effettivamente
-tre variabili di tipo \type{FILE *} che possono essere usate come tutte le
-altre, ad esempio si può effettuare una redirezione dell'output di un
-programma con il semplice codice:
+Nelle \acr{glibc} \var{stdin}, \var{stdout} e \var{stderr} sono
+effettivamente tre variabili di tipo \type{FILE *} che possono essere
+usate come tutte le altre, ad esempio si può effettuare una redirezione
+dell'output di un programma con il semplice codice:
 \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
     fclose (stdout);
     stdout = fopen ("standard-output-file", "w");
 \end{lstlisting}
-ma in altri sistemi possono essere definite come macro, se si hanno problemi
-di portabilità e si vuole essere sicuri, diventa opportuno usare la funzione
-\func{freopen}.
+ma in altri sistemi possono essere definite come macro, se si hanno
+problemi di portabilità e si vuole essere sicuri, diventa opportuno
+usare la funzione \func{freopen}.
 
 
 \subsection{Le modalità di bufferizzazione}
 \label{sec:file_buffering}
 
-La bufferizzazione è una delle caratteristiche principali della interfaccia
-degli stream; lo scopo è quello di ridurre al minimo il numero di system call
-(\func{read} o \func{write}) eseguite nelle operazioni di input/output. Questa
-funzionalità è assicurata automaticamente dalla libreria, ma costituisce anche
-una degli aspetti più comunemente fraintesi, in particolare per quello che
-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'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
-cosa ovviamente ha rilevanza inferiore, dato che i dati letti sono sempre gli
-stessi; in caso di scrittura invece, quando si ha un accesso contemporaneo
-allo stesso file (ad esempio da parte di un altro processo) si potranno vedere
-solo le parti effettivamente scritte, e non quelle ancora presenti nel buffer.
-
-Allo stesso modo, se si sta facendo dell'input/output interattivo bisognerà
-tenere presente le caratteristiche delle operazioni di scarico dei dati,
-poiché non è detto che ad una scrittura sullo stream corrisponda una immediata
-scrittura sul dispositivo.
-
-Per rispondere ad esigenze diverse, lo standard definisce tre distinte modalità
-in cui può essere eseguita la bufferizzazione, delle quali occorre essere ben
-consapevoli, specie in caso di lettura e scrittura da dispositivi interattivi:
+La bufferizzazione è una delle caratteristiche principali della
+interfaccia degli stream; lo scopo è quello di ridurre al minimo il
+numero di system call (\func{read} o \func{write}) eseguite nelle
+operazioni di input/output. Questa funzionalità è assicurata
+automaticamente dalla libreria, ma costituisce anche una degli aspetti
+più comunemente fraintesi, in particolare per quello che 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}
+dei dati, dall'inglese \textit{flush}) 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 cosa ovviamente ha rilevanza inferiore,
+dato che i dati letti sono sempre gli stessi; in caso di scrittura
+invece, quando si ha un accesso contemporaneo allo stesso file (ad
+esempio da parte di un altro processo) si potranno vedere solo le parti
+effettivamente scritte, e non quelle ancora presenti nel buffer.
+
+Allo stesso modo, se si sta facendo dell'input/output interattivo
+bisognerà tenere presente le caratteristiche delle operazioni di scarico
+dei dati, poiché non è detto che ad una scrittura sullo stream
+corrisponda una immediata scrittura sul dispositivo.
+
+Per rispondere ad esigenze diverse, lo standard definisce tre distinte
+modalità in cui può essere eseguita la bufferizzazione, delle quali
+occorre essere ben consapevoli, specie in caso di lettura e scrittura da
+dispositivi interattivi:
 \begin{itemize}
 \item \textit{unbuffered}: in questo caso non c'è bufferizzazione ed i
   caratteri vengono trasmessi direttamente al file non appena possibile
   (effettuando immediatamente una \func{write}).
-\item \textit{line buffered}: in questo caso i caratteri vengono normalmente
-  trasmessi al file in blocco ogni volta che viene incontrato un carattere di
-  \textit{newline} (il carattere ASCII \verb|\n|).
-\item \textit{fully buffered}: in questo caso i caratteri vengono trasmessi da
-  e verso il file in blocchi di dimensione opportuna.
+\item \textit{line buffered}: in questo caso i caratteri vengono
+  normalmente trasmessi al file in blocco ogni volta che viene
+  incontrato un carattere di \textit{newline} (il carattere ASCII
+  \verb|\n|).
+\item \textit{fully buffered}: in questo caso i caratteri vengono
+  trasmessi da e verso il file in blocchi di dimensione opportuna.
 \end{itemize}
 
-Lo standard ANSI C specifica inoltre che lo standard output e lo standard
-input siano aperti in modalità \textit{fully buffered} quando non fanno
-riferimento ad un dispositivo interattivo, e che lo standard error non sia mai
-aperto in modalità \textit{fully buffered}.
+Lo standard ANSI C specifica inoltre che lo standard output e lo
+standard input siano aperti in modalità \textit{fully buffered} quando
+non fanno riferimento ad un dispositivo interattivo, e che lo standard
+error non sia mai aperto in modalità \textit{fully buffered}.
 
 Linux, come BSD e SVr4, specifica il comportamento di default in maniera
 ancora più precisa, e cioè impone che lo standard error sia sempre
@@ -353,8 +359,8 @@ 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{a blocchi} in cui legge/scrive un blocco di dati alla
-  volta, vedi \secref{sec:file_block_io}.
+\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}.
@@ -415,12 +421,75 @@ 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 a blocchi}
-\label{sec:file_block_io}
+\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.
+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 trasferire su file
+blocchi di dati binari in maniera compatta e veloce; un primo caso di uso
+tipico è quello in cui si salva un vettore (o un certo numero dei suoi
+elementi) 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}
+in questo caso devono essere specificate le dimensioni di ciascun
+elemento ed il numero di quelli che si vogliono scrivere. Un secondo
+caso è invece quello in cui si vuole trasferire su file una struttura;
+si avrà allora una chiamata 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}
+in cui si specifica la dimensione dell'intera struttura ed un solo
+elemento. In realtà quello che conta nel trasferimento dei dati sono le
+dimensioni totali, che sono sempre pari al prodotto \func{size * nelem},
+la sola differenza è che le funzioni non ritornano il numero di bytes
+scritti, ma il numero di elementi. 
+
+La funzione \func{fread} legge sempre un numero intero di elementi, se
+si ha un errore o si incontra la fine del file l'oggetto letto
+parzialmente viene scartato
+
+
+Benché queste funzioni assicurino 
 
 
 \subsection{Input/output a caratteri singoli}