Java offre numerose classi per la gestione dell’input/output da sorgenti dati. Le sorgenti possono essere le più svariate: la memoria Ram del computer, Internet, un dispositivo permanente di memorizzazione dati, la tastiera ecc.
L’approccio usato è denominato “a cipolla”, ossia, ci sono delle classi base (dette stream) che rappresentano sorgenti dati o dispositivi, e altre classi che avvolgono i precedenti per aggiungere funzionalità (Buffered, Filter, etc). In questo modo è possibile configurare il canale di comunicazione con tutte e sole le funzionalità che servono nel caso specifico.
Uno stream rappresenta quindi una sequenza ordinata di dati:
- monodirezionale (o è input o è output). Input se le entità fluiscono dall’esterno verso la nostra applicazione. Output se le entità fluiscono dalla nostra applicazione verso l’esterno.
- che trasferisce byte (o anche caratteri). Ossia se i dati sono in formato testo allora si trasferiscono caratteri, altrimenti byte.
Secondo queste due filosofie possiamo individuare due gerarchie di classi per l’I/O, una basata sulle classi InputStream e OutputStream (per i byte) e l’altra basata su Reader per l’input e Writer per l’output (per i caratteri), tutte derivate dalla classe Object.
Le classi InputStream/OutputStream sono la radice di tutte le classi di byte e le classi Reader/Writer sono la radice di tutte le classi basate sui flussi di caratteri.
Da queste classi sono derivate:
- Filter, sono tutti quei flussi ai quali è applicata una operazione di filtraggio; possono anche essere concatenati per produrre filtraggi sofisticati.
- Buffered, aggiungono, agli stream base, un buffer (deposito di dati in memoria su cui si può leggere o scrivere) in modo che le operazioni di read e write risultino più veloci , dato che non è richiesto l’accesso al file system ad ogni invocazione.
- Piped, sono delle coppie di flussi di input/output che forniscono un meccanismo per comunicare tra due thread differenti, uno che legge e l altro che scrive.
- Flussi Array, possibile utilizzare un array di byte/char come sorgente o destinazione.
In base alla sorgente si sceglie lo stream adatto. Ad esempio, se la sorgente è un array di caratteri si usa la categoria CharArrayReader del flusso. Se invece la sorgente è una stringa la classe più adatta è StringReader. Se è una tastiera (la tastiera è individuata in Java da System.in che da uno stream di Byte) la classe InputStreamReader è più adatta, perche converte una sorgente di byte in una sorgente di caratteri. Per un file di testo si puo scegliere la classe FileReader. Se si vuole leggere il file come una sequenza di righe si usa la BufferedReader, e cosi via.
Gestione File
Esempio: Creazione di un file vuoto
public static void newFile() { String path = "C:/html.txt"; try { File file = new File(path); if (file.exists()) System.out.println("Il file " + path + " esiste"); else if (file.createNewFile()) System.out.println("Il file " + path + " è stato creato"); else System.out.println("Il file " + path + " non può essere creato"); } catch (IOException e) { e.printStackTrace(); } }
Il costruttore della classe File riceve in ingresso il path del file. La creazione di un’istanza di File non genera la creazione fisica del file sul disco fisso; occorre chiamare il metodo createNewFile() per crearlo fisicamente.
Se proviamo a lanciare l’esempio, la prima volta il file verrà creato, la seconda volta invece verrà stampato che il file è già presente poiché il metodo exists restituirà true.
Esempio: Scrittura di un File
public static void writeFile() { String path = "C:/Prova/prova.txt"; try { File file = new File(path); FileWriter fw = new FileWriter(file); fw.write("Questo è il nostro primo file"); fw.flush(); fw.close(); } catch (IOException e) { e.printStackTrace(); } }
Il costruttore della classe FileWriter riceve in ingresso un’istanza della classe File.
I metodi principali della classe FileWriter sono i seguenti:
- write(String) – scrive la stringa sul file.
- close() – chiude il descrittore del file.
Come abbiamo detto in precedenza la classe FileWriter scrive un singolo carattere alla volta. Per ottimizzare l’operazione, possiamo utilizzare la classe BufferedWriter. Per il programmatore non cambia molto ma sicuramente viene ottenuto un vantaggio in termini di prestazioni. Il costruttore della classe BufferedWriter riceve in ingresso un’istanza della classe FileWriter. Il metodo per scrivere è sempre write().
Esempio: Scrittura file con BufferWriter
public static void writeFile2() { String path = "C:/html.txt"; try { File file = new File(path); FileWriter fw = new FileWriter(file); BufferedWriter bw = new BufferedWriter(fw); bw.write("Questo è il nostro primo file"); bw.flush(); bw.close(); } catch (IOException e) { e.printStackTrace(); } }
Esempio: Lettura File di Testo
public static void readFile() { String path = "C:/Prova/prova4.txt"; char[] in = new char[50]; int size = 0; try { File file = new File(path); FileReader fr = new FileReader(file); size = fr.read(in); System.out.println("Caratteri presenti: " + size); System.out.println("Il contenuto del file è il seguente:"); for (int i = 0; i < size; i++) System.out.print(in[i]); fr.close(); } catch (IOException e) { e.printStackTrace(); } }
Il costruttore della classe FileReader riceve in ingresso un’istanza della classe File.
I metodi principali di FileReader sono i seguenti:
- read(char[]) – legge n caratteri dal file, uno alla volta, a partire dalla posizione corrente e li memorizza nell’array di caratteri passato in ingresso. Il metodo restituisce, inoltre, il numero di caratteri letti.
- close() – chiude il descrittore del file.
Così come abbiamo fatto per la scrittura, anche per la lettura possiamo ottenere vantaggi in termini di prestazioni, utilizzando la classe BufferedReader. Il costruttore della classe BufferedReader riceve in ingresso un’istanza della classe FileReader. Il metodo da utilizzare per leggere è sempre read().
Esempio: Lettura File di Testo Con BufferRead
public static void readFile2() { String path = "C:/Prova/prova4.txt"; BufferedReader reader; try { File file = new File(path); FileReader fr = new FileReader(file); reader = new BufferedReader(fr); String line = reader.readLine(); while (line != null) { System.out.println(line); line = reader.readLine(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }