Flussi di Input/Output

Il concetto di flusso (stream)
La gestione dell'I/O in Java si basa sul concetto di stream (flusso):  uno stream di input è un oggetto dal quale è possibile ricevere una sequenza di byte;  uno stream di output è un oggetto al quale è possibile inviare una sequenza di byte. Gli oggetti sorgenti e destinazioni di byte possono essere periferiche di I/O (memorie di massa, tastiera, monitor,…), connessioni di rete, blocchi di memoria. In particolare, la classe System (package java.lang) definisce tre costanti pubbliche:
• • •

public static final InputStream in; public static final PrintStream out; public static final PrintStream err;

che rappresentano rispettivamente • • • il flusso di input standard (tastiera, per default); il flusso di output standard (monitor, per default); il flusso di errore standard, ossia dove avviene l'output dei messaggi di errore (monitor, per default). di I/O quali

Nota: forse adesso può essere più chiaro l'uso di istruzioni System.out.println(…..) e Scanner console= new Scanner(System.in).

Il package java.io
Java fornisce numerose classi, all'interno del package java.io, per la gestione degli stream. Esistono, però, 4 classi astratte fondamentali: • InputStream e OutputStream per leggere/scrivere stream di byte; • Reader e Writer per leggere/scrivere stream di caratteri. Ognuna di queste classi astratte dà origine a una varietà di classi concrete che leggono/scrivono tipi specifici di stream. La distinzione tra stream di byte e stream di caratteri è necessaria perché i caratteri Java sono rappresentati con due byte anziché con uno solo (codifica Unicode).
Obj ect

InputStream

OutputStream

Reader

Writer

1
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

Le figure seguenti descrivono rispettivamente la gerarchia degli stream di byte e degli stream di caratteri:

Gerarchia degli stream di byte

Gerarchia degli stream di caratteri

2
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

Le classi derivate dalle classi base si dividono in due categorie: • Da un lato ci sono classi che, senza aggiungere funzionalità, specializzano le classi astratte rispetto alla sorgente/destinazione dei flussi. Ad esempio, per l'input la sorgente del flusso può diventare un file o un buffer. • Dall'altro lato, ci sono classi (dette filtri) che, non preoccupandosi della sorgente/destinazione dei flussi, specializzano e aumentano le funzionalità delle classi astratte per fare in modo di poter leggere/scrivere non soltanto stream di byte o di caratteri, ma dati strutturati, quali i tipi primitivi di Java o interi oggetti. Le classi concrete possono poi essere composte per ottenere un oggetto che offra tutte le funzionalità richieste. La procedura da seguire è la seguente: 1. Per prima cosa si crea un oggetto da una classe che rappresenta la sorgente/destinazione del flusso di dati. 2. Successivamente si crea un oggetto da una classe filtro, passando al costruttore l'oggetto stream precedentemente creato. 3. Infine, si possono eseguire ulteriori incapsulamenti della classe filtro in un'altra classe filtro al fine di ottenere tutte le funzionalità necessarie. Esempio Supponiamo di voler scrivere un numero intero sul file "interi.dat". La destinazione di tale operazione è il file, pertanto creiamo un oggetto della classe FileOutputStream (punto 1): FileOutputStream fout= new FileOutputStream("interi.dat"); La classe FileOutputStream non consente però di scrivere interi (32 byte) ma solo byte, quindi per ottenere questa ulteriore funzionalità dobbiamo utilizzare la classe filtro DataOutputStream (punto2): DataOutputStream out= new DataOutputStream(fout); La tecnica dell'incapsulamento è maggiormente visibile se si utilizza la seguente scrittura: DataOutputStream out= new DataOutputStream(new FileOutputStream ("interi.dat")); Nel seguito vedremo alcuni esempi notevoli di I/O, insieme ad una descrizione più dettagliata delle classi coinvolte.

3
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

Esempio 1: I/O di interi su file
Scriviamo un semplice programma di prova che inserisce un numero intero in un file e poi lo rilegge stampandolo a video. Successivamente viene inserito un altro numero intero, riletto e stampato nuovamente.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import java.io.*; public class Esempio1 { public static void main(String args[]) { //stream di input FileInputStream fin; DataInputStream in; //stream di output FileOutputStream fout; DataOutputStream out; try { //apriamo il file in scrittura fout = new FileOutputStream("interi.dat"); out = new DataOutputStream(fout); //inseriamo un intero nel file interi.dat out.writeInt(15); //chiudiamo il file out.close(); //apriamo il file in lettura fin = new FileInputStream("interi.dat"); in = new DataInputStream(fin); //leggiamo e visualizziamo l'intero inserito System.out.println(in.readInt()); //chiudiamo il file in.close(); //inseriamo un altro intero alla fine del file fout = new FileOutputStream("interi.dat",true); out = new DataOutputStream(fout); out.writeInt(30); out.close(); //leggiamo e visualizziamo tutto il file fin = new FileInputStream("interi.dat"); in = new DataInputStream(fin); for (int i = 1; i <= 2; i++) { System.out.println(in.readInt()); } in.close(); } catch(IOException ecc) { System.out.println("IOException: "+ ecc.getMessage()); }

} }

4
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

Le classi FileInputStream e FileOutputStream
FileInputStream e FileOutputStream sono sottoclassi concrete dirette di InputStream e OutputStream, così definite: • •
public class FileInputStream extends InputStream public class FileOutputStream extends OutputStream

Esse consentono, rispettivamente, di creare un flusso di input o output associato a un file del disco. • Accedere in lettura a un file Le operazioni necessarie per accedere in lettura a un file, sono: 1. aprire il file in lettura; 2. elaborare i dati; 3. chiudere il file, al termine dell'elaborazione. 1. Per aprire il file in lettura, e quindi associare ad esso un flusso di input, occorre creare un oggetto della classe FileInputStream con uno dei costruttori:
public FileInputStream (String nomefile) throws FileNotFoundException public FileInputStream (File file) throws FileNotFoundException

Il file nomefile deve esistere nella directory di lavoro; se non esiste o non può essere aperto in lettura, viene lanciata un'eccezione di tipo FileNotFoundException. Per esempio
FileInputStream flussoInput = new FileInputStream("dati.dat");

2. L'elaborazione dei dati prevede, tra le altre cose, la lettura di byte dal file mediante il metodo read(). 3. Per chiudere il file si utilizza il metodo
public void close() throws IOException

che rilascia ogni risorsa di sistema associata allo stream, se possibile, altrimenti lancia un'eccezione di tipo IOException. Per esempio
flussoInput.close();

• Accedere in scrittura a un file Le operazioni necessarie per accedere in scrittura a un file, sono: 1. aprire il file in scrittura; 2. elaborare i dati; 3. chiudere il file, al termine dell'elaborazione. 1. Per aprire il file in scrittura, e quindi associare ad esso un flusso di output, occorre creare un oggetto della classe FileOutputStream con uno dei costruttori: 5
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

public FileOutputStream (String nomefile) throws FileNotFoundException public FileOutputStream (File file) throws FileNotFoundException public FileOutputStream (String nomefile,boolean append) throws FileNotFoundException public FileOutputStream (File file,boolean append) throws FileNotFoundException

Con la prima coppia di costruttori, il file nomefile viene creato, se non esiste; se il file esiste già, il suo contenuto viene cancellato (equivale ad una creazione). Nel caso in cui il file non possa essere aperto in scrittura, viene lanciata un'eccezione di tipo FileNotFoundException. Con la seconda coppia di costruttori è possibile passare il valore true, per il parametro append, se si desidera iniziare a scrivere a partire dalla fine del file. In tal caso il contenuto che il file aveva in precedenza non viene cancellato. Per esempio
FileOutputStream flussoOutput = new FileOutputStream("dati.dat"); FileOutputStream flussoOutput = new FileOutputStream("dati.dat", true);

2. L'elaborazione dei dati prevede, tra le altre cose, la scrittura di byte nel file mediante il metodo write(). 3. Per chiudere il file si utilizza il metodo
public void close() throws IOException

che rilascia ogni risorsa di sistema associata allo stream, se possibile, altrimenti lancia un'eccezione di tipo IOException. Per esempio
flussoOutput.close();

Le classi DataInputStream e DataOutputStream
DataInputStream e DataOutputStream permettono di leggere e scrivere tutti i tipi fondamentali di Java e le stringhe; esse sono così definite: • •
public class DataInputStream extends FilterInputStream implements DataInput public class DataOutputStream extends FilterOutputStream implements DataOutput

Entrambe implementano un'interfaccia: DataInput e DataOutput rispettivamente. Una interfaccia specifica un insieme di requisiti per le classi che ad essa si vogliono uniformare, ed ha le seguenti caratteristiche:  non contiene variabili di istanza ma può definire costanti (public static final);  i metodi sono privi di implementazione e sono public.

6
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

Quando una classe implementa una interfaccia, deve fornire anche il codice di ogni suo metodo. Alcuni metodi della classe DataInputStream metodo
public DataInputStream(InputStream in) public final boolean readBoolean() throws IOException public final char readChar() throws IOException public final int readInt() throws IOException public final double readDouble() throws IOException

funzionalità
crea uno stream per la lettura di dati dal flusso di input passato come parametro legge un valore booleano dal flusso di input in legge un carattere dal flusso di input in legge un intero dal flusso di input in legge un double dal flusso di input in

Alcuni metodi della classe DataOutputStream metodo
public DataOutputStream(OutputStream out) public final void writeBoolean(Boolean b) throws IOException public final void writeChar(int v) throws IOException public final void writeInt(int v) throws IOException public final void writeDouble(double v) throws IOException

funzionalità
crea uno stream per la scrittura di dati sul flusso di output passato come parametro scrive il valore booleano b sul flusso di output out scrive il carattere v sul flusso di output out scrive l'intero v sul flusso di output out legge il double v sul flusso di output out

Esempio 2: I/O di stringhe su file
Il programma seguente scrive e legge due stringhe su/da file. L'I/O di una stringa avviene carattere per carattere.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 import java.io.*; public class Esempio2 { public static void main(String args[]) { FileInputStream fin; FileOutputStream fout; DataInputStream in; DataOutputStream out; char spazio = ' '; String s1,s2; try { //inizializza il file string.dat con due stringhe fout = new FileOutputStream("string.dat"); out = new DataOutputStream(fout);

7
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

s1 = "ciao"; s2 = "bye"; for (int i = 0; i < s1.length(); i++) { out.writeChar(s1.charAt(i)); } out.writeChar(spazio); for(int i = 0; i <s2.length(); i++) { out.writeChar(s2.charAt(i)); } out.writeChar(spazio); out.close(); //rilegge le stringhe dal file fin = new FileInputStream("string.dat"); in = new DataInputStream(fin); s1 = ""; char ch = in.readChar(); while (ch != ' ') { s1 = s1+ch; ch = in.readChar(); } System.out.println(s1); s2 = ""; ch = in.readChar(); while (ch != ' ') { s2 = s2+ch; ch = in.readChar(); } System.out.println(s2); in.close(); } catch(IOException ecc) { System.out.println("IOException: "+ ecc.getMessage()); } } }

Esempio 3: I/O di record su file
Il programma seguente scrive e poi rilegge due record su/da file.
01 02 03 04 05 06 07 08 09 import java.io.*; public class Esempio3 { public static void main(String args[]) { Record r1,r2,r3,r4; FileInputStream fin; FileOutputStream fout;

8
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

DataInputStream in; DataOutputStream out; r1 = new Record(1, 'a'); r2 = new Record(2, 'b'); try {

fout = new FileOutputStream("record.dat"); out = new DataOutputStream(fout); r1.scriviRecord(out); r2.scriviRecord(out); out.close(); fin = new FileInputStream("record.dat"); in = new DataInputStream(fin); r3 = new Record(); r4 = new Record(); r3.leggiRecord(in); r4.leggiRecord(in); System.out.println(r3.toString()); System.out.println(r4.toString()); in.close();

} }

} catch(IOException ecc) { System.out.println("IOException: "+ecc.getMessage()); }

class Record { private int codice; private char nome; public Record() {} public Record(int cod, char nom) { codice= cod; nome= nom; } public String toString() { return "Record[codice="+codice+", nome= "+nome+"]"; } public void leggiRecord(DataInput in) throws IOException { codice= in.readInt(); nome= in.readChar(); } public void scriviRecord(DataOutput out) throws IOException { out.writeInt(codice); out.writeChar(nome); }

9
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

71

}

Esempio 4 - Lettura di testo immesso da tastiera
Il seguente codice consente di acquisire il testo immesso da tastiera.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.io.*; public class ConsoleLineReader { public static void main(String args[]) { String input; BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); try { System.out.println("Immetti una stringa"); System.out.println("Premi invio per terminare"); input = in.readLine(); while (!input.equals("")) { System.out.println("ECHO: "+ input); System.out.println("Immetti una stringa"); System.out.println("Premi invio per terminare"); input = in.readLine(); } in.close(); } catch(IOException e) { e.getMessage(); } } }

righe 8-9 Analizziamo l'istruzione
BufferedReader in= new BufferedReader(new InputStreamReader(System.in));

– –

La chiamata new InputStreamReader (System.in) crea un flusso di input di caratteri dal flusso di input standard (la tastiera) System.in; purtroppo è possibile leggere solo un carattere per volta. La classe BufferedReader incapsula il flusso precedentemente creato, consentendo di leggere una linea intera.

righe 14 e 20 L'istruzione
input= in.readLine();

legge una linea di testo dal flusso di input standard. riga 15 Il programma termina quando l'utente preme invio senza aver inserito testo. 10
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

Le classi InputStreamReader e OutputStreamWriter
InputStreamReader e OutputStreamWriter sono sottoclassi concrete dirette di Reader e Writer, così definite: • • public class InputStreamReader extends Reader public class OutputStreamWriter extends Writer

La classe InputStreamReader funge da ponte tra flussi di byte e flussi di caratteri. Essa legge byte e li traduce in caratteri, in accordo con un certo codice. Il codice da utilizzare può essere specificato nel costruttore. Se il codice da adottare non è indicato, viene utilizzato il codice definito per la piattaforma in uso. Nella tabella seguente sono riassunti alcuni metodi utili della classe InputStreamReader. Metodi utili di InputStreamReader metodo
public InputStreamReader(InputStream in) public int read() throws IOException public void close() throws IOException

funzione svolta
crea un flusso di input di caratteri che utilizza la codifica di default della piattaforma legge un singolo carattere chiude il flusso di input

La classe OutputStreamReader funge da ponte tra flussi di caratteri e flussi di byte. Essa legge caratteri e li traduce in byte, in accordo con un certo codice. Il codice da utilizzare può essere specificato nel costruttore. Se il codice da adottare non è indicato, viene utilizzato il codice definito per la piattaforma in uso. Nella tabella seguente sono riassunti alcuni metodi utili della classe OutputStreamWriter. Metodi utili di OutputStreamWriter metodo
public OutputStreamWriter(OutputStream out) public void write(int c) throws IOException public void flush() throws IOException public void close() throws IOException

funzione svolta
crea un flusso di output di caratteri che utilizza la codifica di default della piattaforma scrive un singolo carattere svuota il flusso di output chiude il flusso di output

La classe BufferedReader
Spesso capita di dover leggere stringhe e non singoli caratteri. La classe BufferedReader offre questa ulteriore funzionalità. • public class BufferedReader extends Reader

Nella tabella seguente sono riassunti alcuni metodi utili della classe.

11
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

Metodi utili di BufferedReader metodo
public BufferedReader(Reader in) public int read() throws IOException public String readLine() throws IOException

funzione svolta
crea un flusso di input di caratteri che utilizza un buffer di input di dimensione prefissata legge un singolo carattere – legge una linea di testo – la linea termina con un "line feed" ('\n') o con un "carriage return" ('\r') – il metodo restituisce null se si è raggiunta la fine dello stream chiude il flusso di input

public void close() throws IOException

Incapsulando un oggetto InputStreamReader in un oggetto BufferedReader è quindi possibile leggere stringhe.

Esempio 5 - Scrittura su/Lettura da un file di testo
La classe FileWriter consente di creare un flusso di output di caratteri verso un file di testo. Per poter scrivere su un file i tipi di dati primitivi di Java in formato testo, occorre associare tale flusso ad uno scrittore di stampa, mediante la classe PrintWriter. Il codice seguente scrive sul file "testo.txt" rispettivamente l'intero 124, il double 124.0, il booleano true e la stringa centoventiquattro.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 import java.io.*; public class WriterOnFile { public static void main(String args[]) { try { PrintWriter out = new PrintWriter(new FileWriter("testo.txt"), true); out.println(124); out.println(124.0); out.println(true); out.println("centoventiquattro"); out.close(); } catch(IOException e) { e.getMessage(); } } }

La classe FileReader consente di creare un flusso di input di caratteri da un file di testo. Per poter leggere stringhe da un file di testo, occorre associare il flusso precedentemente creato ad un lettore, mediante la classe BufferedReader. Se conosciamo la semantica dei dati memorizzati su file, possiamo anche convertire le stringhe lette negli opportuni dati (interi, double, ecc.), esattamente come abbiamo fatto per le stringhe immesse da tastiera. 12
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22

import java.io.*; public class ReaderFromFile { public static void main(String args[]) { String line; try { BufferedReader in = new BufferedReader(new FileReader("testo.txt")); line = in.readLine(); while (line != null) { System.out.println("letto da file: "+ line); line= in.readLine(); } in.close(); } catch(IOException e) { e.getMessage(); } } }

La classe PrintWriter
La classe PrintWriter consente di stampare stringhe e numeri in formato testo. •
public class PrintWriter extends Writer

La tabella seguente riporta alcuni metodi utili della classe. Metodi utili di PrintWriter metodo
public PrintWriter(Writer out) public PrintWriter(Writer out, boolean autoFlush) public PrintWriter(OutputStream out)

funzione svolta
crea uno scrittore di stampa crea uno scrittore di stampa con autoflush di riga se autoflush è true (i metodi di stampa svuoteranno il buffer di output) crea uno scrittore di stampa da un OutputStream esistente, creando automaticamente l'OutputStreamWriter intermedio necessario come il precedente, ma consente di decidere se inserire o meno l'opzione autoflush scrive un singolo carattere stampa un oggetto visualizzando la stringa ottenuta con toString stampa una stringa stampa un array di caratteri stampa un carattere stampa un intero; la stringa è ottenuta con String.valueOf(int) stampa un long; la stringa è ottenuta con String.valueOf(long)

public PrintWriter(OutputStream out, boolean autoFlush) public void write(int c) public void print(Object obj) public public public public void void void void print(String s) print(char[] s) print(char c) print(int i)

public void print(long l)

13
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

public void print(float f) public void print(double d) public void print(boolean b) public void println() public void flush() public void close()

stampa un float; la stringa è ottenuta con String.valueOf(float) stampa un double; la stringa è ottenuta con String.valueOf(double) stampa un boolean; la stringa è ottenuta con String.valueOf(boolean) termina la linea corrente scrivendo un separatore di linea ("va a capo") svuota il flusso di output chiude il flusso di output

Osservazioni – Per ogni metodo print esiste il corrispondente metodo println. – I metodi print e println non lanciano eccezioni. Per vedere gli eventuali errori nello stream occorre chiamare il metodo checkError:
public boolean checkError()

che restituisce true se si è verificato un errore.

Le classi FileReader e FileWriter
Supponiamo di voler leggere da un file di testo. Per quanto detto in precedenza, dobbiamo creare un oggetto da una classe che rappresenta la sorgente del flusso di caratteri (il file):
(1) FileInputStream fin= new FileInputStream("nomefile.txt");

Successivamente dobbiamo incapsulare lo stream ottenuto in una classe che consenta di leggere caratteri (InputStreamReader):
(2) InputStreamReader in= new InputStreamReader(fin);

Analogamente, per scrivere su un file di testo occorre definire la destinazione del flusso di caratteri
(3) FileOutputStream fout= new FileOutputStream("nomefile.txt");

e incapsulare lo stream ottenuto nella classe OutputStreamWriter
(4) OutputStreamWriter out= new OutputStreamWriter(fout);

Dato che l'associazione di un lettore/scrittore a un file è un'operazione molto comune, Java mette a disposizione due classi che rispondono a questa esigenza: FileReader e FileWriter. Le istruzioni (1) e (2) possono essere sostituite con l'istruzione equivalente:
FileReader in= new FileReader("nomefile.txt");

Analogamente, le istruzioni (3) e (4) possono essere sostituite con: 14
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

FileWriter out= new FileWriter("nomefile.txt");

Nelle tabelle seguenti sono riassunti alcuni utili metodi delle classi FileReader e FileWriter. Metodi utili di FileReader metodo
public throws public throws FileReader(String fileName) FileNotFoundException FileReader(File file) FileNotFoundException

funzione svolta
crea un lettore per il file specificato (il file deve esistere) crea un lettore per il file specificato (il file deve esistere)

Metodi utili di FileWriter metodo
public FileWriter(String fileName) throws IOException public FileWriter(String fileName, boolean append) throws IOException

funzione svolta
crea uno scrittore per il file specificato; se il file esiste già, cancella i dati in esso contenuti crea uno scrittore per il file specificato, in modalità append (se append è true); se il file esiste già, non cancella i dati in esso contenuti crea uno scrittore per il file specificato; se il file esiste già, cancella i dati in esso contenuti crea uno scrittore per il file specificato, in modalità append (se append è true); se il file esiste già, non cancella i dati in esso contenuti

public FileWriter(File file) throws IOException public FileWriter(File file, boolean append) throws IOException

Quest'opera è stata rilasciata con licenza Creative Commons Attribution-ShareAlike 3.0 Unported. Per leggere una copia della licenza visita il sito web http://creativecommons.org/licenses/by-sa/3.0/ o spedisci una lettera a Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.

15
Bocchi Cinzia Ultimo aggiornamento: 30/04/2012

Sign up to vote on this title
UsefulNot useful