Operazioni di I/O da console e da file in C standard Sono eseguite attraverso funzioni di libreria Non differiscono molto se rivolte a console o a file Richiedono l’header <stdio.h>
Operazioni standard di I/O da console - 1 Sono il più possibile portabili, per cui Non comprendono la gestione dello schermo né il posizionamento del cursore Non comprendono la gestione di sistemi a finestre (p.es. Windows) Permettono di scrivere in formato di puro testo (p.es. console DOS o Unix)
Operazioni standard di I/O da console - 2 Accettano input dallo standard input (stdin, generalmente la tastiera) Producono output verso lo standard output (stdout, generalmente lo schermo) I canali standard di input e output non sono altro che file “speciali”
Output su console : Caratteri int putchar( int c ); Converte il carattere in int e ignora il byte più significativo Restituisce il carattere scritto oppure EOF in caso di errore
Input da console : Caratteri - 1 int getchar( void ); int getche( void ); int getch( void ); Convertono il carattere in int e ignorano il byte più significativo Restituiscono il carattere letto oppure EOF in caso di errore
Input da console : Caratteri - 2 bufferizzata echo ANSI getchar ü getche getch
… char ch, str[81]; int i = 0; do { ch = getche(); // o getch(); o getchar(); fflush(); str[ i++ ] = ch; } while( ch != '.' && i < 80 ); str[i] = '\0'; // va inserito esplicitamente i = 0; while( str[i] != '\0' ) putchar( str[ i++ ] ); console_c
Output su console : Stringhe int puts( const char* str ); Rimpiazza il carattere nullo di terminazione ('\0') con il ritorno a capo ('\n') Riconosce i caratteri speciali ('\t', '\n'… ) Restituisce un intero non negativo oppure EOF in caso di errore
Input da console : Stringhe char* gets( char* str ); Rimpiazza il ritorno a capo ('\n') con il carattere nullo di terminazione ('\0') Restituisce str oppure NULL in caso di errore NON fa alcun controllo sulla dimensione del buffer rappresentato da str
#include <stdio.h> void main() { char str[81]; puts( "\n Scrivi una parola o frase e premi " "<invio> (max.80 caratteri):" ); gets( str ); puts( "\n La riga inserita e\':" ); puts( str ); } console_s
Operazioni standard di I/O da file - 1 Si opera su stream (detti anche canali) Ad ogni stream si associa un file/periferica/dispositivo Lo stream fornisce un livello di astrazione comune per tutti i dispositivi fisici la funzione di scrittura su uno stream resta la stessa indipendentemente dalla periferica sulla quale si deve scrivere (disco, video, stampante, …)
Operazioni standard di I/O da file – 2 Si parla genericamente di file per indicare in realtà un file/dispositivo/periferica Gli stream sono tutti uguali, i file no Se il file lo permette, lo stream prevede operazioni bufferizzate per ottimizzare gli accessi al file operazioni di posizionamento per accedere ai dati in ordine diverso rispetto a quello in cui si trovano nel file
Operazioni standard di I/O da file – 3 Lo stream è rappresentato nel programma da una variabile di tipo FILE* La struttura FILE contiene le informazioni relative all’identificazione, all’utilizzo e allo stato del file Tutte le operazioni di I/O su file avvengono attraverso questa variabile Tra l’operazione di apertura (fopen) e di chiusura (fclose) dello stream esiste un canale di comunicazione tra programma e file
Output su file : Caratteri int fputc( int c, FILE* stream ); int putc( int c, FILE* stream ); Convertono il carattere in int e ignorano il byte più significativo Restituiscono il carattere scritto oppure EOF in caso di errore Spostano la posizione all’interno del file sul prossimo carattere “libero”
Input da file : Caratteri int fgetc( FILE* stream ); int getc( FILE* stream ); Convertono il carattere in int e ignorano il byte più significativo Restituiscono il carattere letto oppure EOF in caso di errore o fine del file Spostano la posizione all’interno del file sul prossimo carattere
… int i; char ch; FILE *fpSrc, *fpDest; if( (fpSrc = fopen( "src.txt", "r" )) == NULL ) { puts( "Errore durante l'apertura di src.txt" ); exit(1); } if( (fpDest = fopen( "dest.txt", "w" )) == NULL ) { puts( "Errore durante la creazione di dest.txt" ); exit(2); file_c - 1
i = 0; while( (ch = fgetc( fpSrc )) != EOF ) { fputc( ch, fpDest ); printf( "\nCarattere n.%d (%c)" "\tPremi un tasto...", i++, ch ); getch(); } fclose( fpDest ); fclose( fpSrc ); puts( "Copia effettuata." ); … file_c – 2
Output su file : Stringhe int fputs( const char* str, FILE* stream ); Copia la stringa sul file sino al carattere nullo di terminazione (escluso) Riconosce i caratteri speciali Restituisce un intero non negativo oppure EOF in caso di errore Sposta la posizione all’interno del file sul prossimo carattere “libero”
Input su file : Stringhe - 1 char* fgets( char* str, int n, FILE* stream ); Copia la stringa dal file sino al primo evento/carattere tra i seguenti: carattere di ritorno a capo (incluso) numero massimo n-1 di caratteri letti carattere di fine del file (escluso)
Input su file : Stringhe - 2 Implementa attraverso n un controllo sulla dimensione del buffer (str) Aggiunge il carattere nullo di terminazione alla fine della stringa nel buffer Restituisce str oppure NULL in caso di errore o fine del file Sposta la posizione all’interno del file sul prossimo carattere “libero”
… int i, n = 81; char* str[81]; FILE *fpSrc, *fpDest; i = 0; while( fgets( str, n, fpSrc ) != NULL ) { fputs( str, fpDest ); printf( "\nRiga n.%d (%s)" "\tPremi un tasto...", i++, str ); getch(); } file_s
Output su file : Byte size_t fwrite( void* mem, size_t size, size_t n, FILE* stream ); Copia dalla zona di memoria sul file n elementi grandi size byte ognuno Restituisce il numero di elementi scritti (minore di n in caso di errore) Sposta la posizione all’interno del file del numero di byte scritti
Input da file : Byte size_t fread( void* mem, size_t size, size_t n, FILE* stream ); Copia dal file nella zona di memoria n elementi grandi size byte ognuno Restituisce il numero di elementi letti (minore di n in caso di errore o fine del file) Sposta la posizione all’interno del file del numero di byte letti
… struct memo_t { char nome[81]; int anni; }; int vet[3] = { 8, -1, 5 }; double db = 3.2e-6; struct memo_t pers; FILE* fpDest; fwrite( vet, sizeof( int ), 3, fpDest ); fwrite( &db, sizeof( double ), 1, fpDest ); fwrite( &pers, sizeof( struct memo_t ), 1, fpDest ); file_b
Modo testo e modo binario - 1 Uno stream può essere aperto in modalità testo oppure in modalità binaria ('b') Uno stream di testo è visto come una sequenza di caratteri una sequenza di righe separate dal ritorno a capo Uno stream binario è visto come una sequenza indistinta di byte Su entrambi si può scrivere ogni tipo di dati
Modo testo e modo binario - 2 Uno stream di testo PUÒ prevedere operazioni di traduzione (NL ó CR/LF) Può mancare la corrispondenza 1:1 tra caratteri nello stream e caratteri nel file Uno stream binario NON prevede nessuna operazione di traduzione Sussiste la corrispondenza 1:1 tra caratteri nello stream e caratteri nel file
modo_t_b - 1 … char str[81] = "Oggi e\' proprio\nuna bella giornata.\n" "Quasi quasi faccio\nun giro in bici."; char ch; int n; FILE *fpText, *fpBin; fpBin = fopen( "esempio.dat", "wb" ); fpText = fopen( "esempio.txt", "wt" ); printf( "\nLunghezza della stringa in memoria: " "%d byte\n", strlen( str ) );
modo_t_b - 2 n = fwrite( str, sizeof( char ), strlen( str ), fpBin ); printf( "\n%d caratteri scritti su esempio.dat\n", n ); n = fwrite( str, sizeof( char ), strlen( str ), fpText ); printf( "\n%d caratteri scritti su esempio.txt", n ); printf( "\nLunghezza di esempio.dat: %d byte\n", ftell( fpBin ) ); printf( "\nLunghezza di esempio.txt: %d byte", ftell( fpText ) ); fclose( fpText ); fclose( fpBin ); …
modo_t_b - 3 … fpBin = fopen( "esempio.dat", "rb" ); fpText = fopen( "esempio.txt", "rb" ); // come binario puts( "\nByte scritti in esempio.dat:" ); n = 0; while( fread( &ch, sizeof( char ), 1, fpBin ) != 0 ) { printf( "%02X ", ch ); if( ch == 0x0A ) puts( "" ); // va solo a capo n++; } printf( "\nTotale byte: %d\n", n );
modo_t_b - 4 puts( "\nByte scritti in esempio.txt:" ); n = 0; while( fread( &ch, sizeof( char ), 1, fpText ) != 0 ) { printf( "%02X ", ch ); if( ch == 0x0A ) puts( "" ); // va solo a capo n++; } printf( "\nTotale byte: %d\n", n ); fclose( fpBin ); fclose( fpText ); …
modo_t_b - 5 Lunghezza della stringa in memoria: 71 byte 71 caratteri scritti su esempio.dat 71 caratteri scritti su esempio.txt Lunghezza di esempio.dat: 71 byte Lunghezza di esempio.txt: 74 byte Byte scritti in esempio.dat: 4F 67 67 69 20 65 27 20 70 72 6F 70 72 69 6F 0A 75 6E 61 20 62 65 6C 6C 61 20 67 69 6F 72 6E 61 74 61 2E 0A 51 75 61 73 69 20 71 75 61 73 69 20 66 61 63 63 69 6F 0A 75 6E 20 67 69 72 6F 20 69 6E 20 62 69 63 69 2E Totale byte: 71 Byte scritti in esempio.txt: 4F 67 67 69 20 65 27 20 70 72 6F 70 72 69 6F 0D 0A 75 6E 61 20 62 65 6C 6C 61 20 67 69 6F 72 6E 61 74 61 2E 0D 0A 51 75 61 73 69 20 71 75 61 73 69 20 66 61 63 63 69 6F 0D 0A Totale byte: 74
Accesso sequenziale e accesso diretto Esistono due modalità con cui accedere ai dati contenuti in un file: Accesso sequenziale Per accedere all’i-esimo dato occorre accedere a tutti quelli precedenti Orientato ad operazioni globali Accesso diretto o random Si può accedere direttamente al dato i-esimo Orientato ad operazioni locali
Posizionamento su file - 1 int fseek( FILE* stream, long n, int origine ); Sposta la posizione all’interno del file di n byte a partire dalla posizione specificata dalla macro origine Restituisce 0 in caso di successo oppure un intero diverso da 0 in caso di errore
Posizionamento su file - 2 macro int posizione SEEK_SET dall’inizio del file SEEK_CUR 1 dalla posizione corrente SEEK_END 2 dalla fine del file
Posizionamento su file - 3 void rewind( FILE* stream ); Riporta all’inizio (“riavvolge”) la posizione all’interno del file long ftell( FILE* stream ); Restituisce la posizione corrente all’interno del file oppure -1 in caso di errore In genere è usata in coppia con fseek
… int vet[5] = { 21, -1, 8, -17, 33 }; int n, i = 3; FILE *fpDest; fwrite( vet, sizeof( int ), 5, fpDest ); fseek( fpDest, i*sizeof( int ), SEEK_SET ); fread( &n, sizeof( int ), 1, fpDest ); printf( "\nElemento n.%d: %d", i, n ); fseek( fpDest, 0, SEEK_END ); n = (int) ftell( fpDest ); // numero di byte del file printf( "\nNumero di elementi: %d\n", n/sizeof( int ) ); file_pos1
Posizionamento su file - 4 int fgetpos( FILE* stream, fpos_t* pos ); int fsetpos( FILE* stream, fpos_t* pos ); Legge/scrive il valore dell’indicatore della posizione corrente all’interno del file Restituiscono 0 se l’operazione è riuscita oppure un intero diverso da 0 in caso di errore DEVONO essere usate in coppia perché l’indicatore usa un formato interno speciale
… int vet[5] = { 21, -1, 8, -17, 33 }; int num; fpos_t segnaposto; FILE *fpDest; fwrite( vet, sizeof( int ), 3, fpDest ); fgetpos( fpDest, &segnaposto ); fwrite( &vet[3], sizeof( int ), 2, fpDest ); // oppure fwrite( vet+3, … fsetpos( fpDest, &segnaposto ); fread( &num, sizeof( int ), 1, fpDest ); printf( "\nL\'elemento in posizione %d " "vale %d\n", segnaposto, num ); file_pos4
Controlli su file int feof( FILE* stream ); Restituisce un intero diverso da 0 se la posizione corrente è sulla fine del file, viceversa restituisce 0 int ferror( FILE* stream ); Restituisce un intero diverso da 0 se ci sono stati errori sul file, viceversa restituisce 0
… char str[100]; FILE *fpSrc while( !feof( fpSrc ) ) { fread( str, sizeof( char ), 100, fpSrc ); if( ferror( fpSrc ) ) { puts( “Errore durante la lettura!" ); break; } puts( “Fine del file raggiunta." ); fclose( fpSrc ); file_verif
Buffer associato a file int fflush( FILE* stream ); Svuota sul file il buffer temporaneo usato per ottimizzare l’accesso al file Se si passa NULL come argomento opera su tutti i file aperti in scrittura Restituisce 0 se l’operazione è riuscita oppure EOF in caso di errore
Redirezione di file FILE* freopen( const char* path, const char* mode, FILE* stream ); Chiude il file associato a stream e assegna quest’ultimo al file avente percorso path Restituisce un puntatore al file appena aperto oppure NULL in caso di errore (il file iniziale viene comunque chiuso)
… FILE *fpDest; fpDest = freopen( "output.txt", "w", stdout ); fputs( "Questa andra\' sul file output.txt\n", fpDest ); puts( "Anche questa fatta col semplice \'puts\'\n" ); printf( "E anche con \'printf\': %d.\n", 5 ); fclose( fpDest ); fputs( "E questa verso \'stdout\' ?\n", stdout ); fputs( "E questa verso \'stderr\' ?\n", stderr ); freopen
Operazioni di I/O in C ++ Obiettivi del sistema di I/O Gli stream Tipi di stream File Classi di stream Apertura e chiusura Lettura e scrittura
Obiettivi • Descrive come sottoporre a overload gli operatori << e >> in modo da ottenere l’input e l’output di oggetti appartenenti a classi scritte dal programmatore • Come formattare l’output • Come utilizzare i manipolatori di I/O • Il funzionamento dell’I/O su file
Stream • È un’interfaccia logica mediante la quale vengono effettuate le operazioni di I/O • Produce e consuma informazioni ed è collegato ad un dispositivo fisico tramite il sistema di I/O del C++ – È comune per i vari dispositivi che costituiscono il computer – Il comportamento è identico per tutti gli stream anche se sono collegati a dispositivi differenti. • Per il programmatore tutti i dispositivi risultano simili tra loro – Le operazioni di I/O possono operare su tutti i tipi di dispositivo.
Stream predefinti • cin: associato allo standard input • cout: associato allo standard output – collegati a cout: • clog: è bufferizzato, l’output è scritto solo dopo che il buffer è pieno • cerr: non è bufferizzato, l’oiutout è scritto non appena è inviato • versioni a caratteri estesi( a 16 bit) – wcin, wcout, wclog, wcerr
Classi di stream • Il supporto al sistema di I/O del C++ è fornito dall’intestazione standard • <iostream> – definisce un insieme di gerarchie di classi che supportano le operazioni di I/O • Si fonda su due gerarchie di classi correlate ma diverse: – basic_streambuf • classe di basso livello • fornisce supporto per le operazioni di basso livello – basic_ios • classe di alto livello • fornisce formattazione, controllo di errore e informazioni di stato in relazione all’I/O
Classi a caratteri • Ios – contiene funzioni e variabili membro che controllano il funzionamento di uno stream – costituisce la classe base per molte classi derivate: • streambuf • ios • istream • ostream • iostream • fstream Stream associati ai file • ifstream “ • ofstream “
Overload degli operatori di I/O • È possibile effettuare l’overload degli operatori – << operatore di inserimento: • inserisce caratteri in uno stream – >>operatore di estrazione: • estrae caratteri da uno stream – In <iostream>, gli operatori << e >> sono soggetti ad overload per effettuare I/O su tutti i tipi di dati predefiniti
• l sistema di I/O può essere usato per effettuare I/O su file • È necessaria l’intestazione <fstream> Apertura • Apertura di uno stream di input – Occorre dichiarare che appartiene alla classe <ifstream> • Apertura di uno stream di output – Occorre dichiarare che appartiene alla classe <ofstream> • Apertura di uno stream che eseguirà operazioni di input e di output – Occorre dichiarare che appartiene alla classe <fstream>
Funzione open • open() • associa uno stream ad un file • membro di tutte le tre classi stream – Argomenti della funzione • Nomefile: può contenere anche il path • Modalità di apertura: determina come verrà aperto il file – Il valore è determinato da openmode – app: aggiunge l’output alla file del file – ate: ricerca la fine del file ma lo apre in modo che le operazioni possano avvenire in qualunque posizione – binary: apre il file in modalità binaria – in: il file è in grado did produrre dati – out: specifica che il file è in grado di ricevere output – trunc: distrugge il contenuto del file preesistente – OR di due qualsiasi dei precedenti (|)
Esempio apertura un file di output: ofstream out; out.open(“test”); la modalità di apertura in alcuni compilatori è per default un valore appropriato al tipo di stream aperto Apertura mediante costruttore • ifstream, ofstream e fstream hanno funzioni costruttore che provvedono automaticamente all’apertura del file • i costruttori hanno gli stessi parametri e valori di default della funzione open Esempio Apertura di un file per l’input mediante il costruttore ifstream mystream(“test”); Chiusura Si usa la funzione close out.close(); La funzione non ha parametri e non restituisce valori
Esempio di scrittura su un file testo Lettura e scrittura • Si usano gli operatori << >> Esempio di scrittura su un file testo #include <iostream> #include <fstream> using namespace std; void main() { ofstream out(“test”); if (!out) {cout << “impossible aprire il file”;} out<<10<<“ “<123.23<<“\n”; out<< file di testo”; out.close(); }
Esempio di lettura da un file testo #include <iostream> #include <fstream> using namespace std; void main() { char ch; int i; float f; char str[80]; ifstream in(“test”); if (!in) { cout<< “ Impossible aprire il file”;} in>>i; in>>f; in >>ch; in >> str; cout<< i <<“ “ <<f<<“ “ <<ch<<“\n”; cout<<str, in.close(); }
Lettura e scrittura di file binari non formattati • Può avvenire in due modi: • Usando funzioni – a byte • put () • get() – a blocchi • read() • write() Uso della get #include<iostream> #include<fstream> using namespace std; void main(){ char ch; ifstream in”test” ios::in | ios::binary); if(!in){ cout<<“impossibile aprire il file”;} while (in) {// in è falso quando si raggiunge la fine del file in.get(ch); if(in) cout <<ch; } in.close();
Uso della funzione put #include<iostream> #include<fstream> using namespace std; void main(){ char *s=“stringa di prova”; ofstream out(”test”, ios::out | ios::binary); if(!out){ cout<<“impossibile aprire il file”;} while (*s) out.put(*s++); out.close(); }
Uso della funzione write Funzioni read e write • read: • Legge un numero num di byte dallo stream associato e li mette nel buffer • write: • Scrive sullo stream associato un numero num di byte provenienti dal buffer Uso della funzione write #include<iostream> #include<fstream> using namespace std; void main() {char s[80]=“stringa”; int i; ofstream out(“test”, ios::out | ios::binary); if(!out) {cout<<“Impossibile aprire il file”;} out.write(s, sizeof(s)); cout<<“ scrittura “<<“\n”; in.close(); }
Uso della funzione read #include<iostream> #include<fstream> using namespace std; void main() { char s[80]; int i; ifstream in(“test”, ios::in | ios::binary); if(!in) {cout<<“Impossibile aprire il file”;} in.read(s, sizeof(s)); cout<<“ scrittura “<<“\n”; cout<<s; in.close(); }
Accesso casuale • La lettura e la scrittura può avvenire in modo casuale. • Si usano le funzioni seekg, seekp, specificando i valori di: – Offset: un intero che specifica il numero di byte di cui spostarsi – Origine: la posizione a partire dalla quale spostarsi ios::beg ios::cur ios::end Puntatori al file • get: – specifica in che punto del file avverrà la successiva operazione di input • put – specifica in che punto del file avverrà la successiva operazione di output • Ogni volta che viene effettuata un’operazione il puntatore appropriato viene fatto avanzare automaticamente
Esempi di uso di seekg e seekp Uso delle funzioni seekg e seekp • Seekg() – Sposta il puntatore get correntemente associato al file di un numero offset di byte a partire dal punto specificato da origine • Seekp() – Sposta il puntatore put correntemente associato al file di un numero offset di byte a partire dal punto specificato da origine Esempi di uso di seekg e seekp #include<iostream> #include<fstream> using namespace std; void main() { int i; ftsream out(“test”, ios::in | ios::out | ios:: binary); if (!out) {cout<<impossibile aprire il file;} out.seekp(atoi(“test”),ios::beg); out.put(“x”); out.close(); }
Funzioni per determinare la posizione corrente dei puntatori #include<iostream> #include<fstream> using namespace std; void main() { int i; ifstream in(“test”,ios::in|ios::binary); if(!in){cout<<“impossibile aprire il file”;} in.seekg(atoi(“test”),ios::beg); while (in.get(ch)) cout <<ch; } Funzioni per determinare la posizione corrente dei puntatori • pos_type tellg(); • pos_type tellp();