Classi per lettura e scrittura di file

Nei programmi sviluppati sino ad oggi abbiamo usato 
  • output su schermo (con System.out.print[ln])
  • input da tastiera (con la classe ConsoleReader)
Se un programma deve leggere o scrivere grandi quantità di dati, è conveniente memorizzare questi dati in files. 

Java fornisce classi e metodi per scrivere e leggere dati da files (nel package java.io  [locale, Medialab, Sun]).

I dati possono essere memorizzati in un file in formato

  • testo (sequenza di caratteri, leggibile da esseri umani)
  • binario (sequenza di byte)
Classi per I/O su files Input da file Output su file
Formato testo FileReader FileWriter
Formato binario FileInputStream FileOutputStream

Queste classi gestiscono I/O di caratteri o bytes da files: per dati più complessi (stringhe, numeri) introdurremo altre classi.

Vedremo solo I/O in formato testo, ma quello in formato binario è del tutto analogo.
 
 

 




Lettura di singoli caratteri da file

Per leggere dati da un file, occorre:
  • creare un oggetto della classe FileReader, passando il nome del file al costruttore; 

  •  
  • utilizzare il metodo  public int read() per leggere i caratteri;
  • chiudere il file con il metodo close().
I metodi e il costruttore di FileReader possono lanciare una eccezione di tipo IOException  che rappresenta un errore di I/O. Queste eccezioni sono controllate, e quindi devono essere previste dal programmatore.

Il metodo read() restituisce un intero che può essere

  • -1 se si è arrivati alla fine del file;

  •  
  • un intero tra 0 e 16383, codice di un carattere UNICODE.
Tipicamente, si controlla se il numero letto è diverso da -1 e in questo caso si trasforma in un char usando l'operatore di cast.



Esempio di lettura da file


CopyRead.java: stampa su video del contenuto di un file (copyread.txt). Le eventuali IOException non vengono gestite dal programma ma rinviate al chiamante.
 
import java.io.*;

public class CopyRead {
public static void main(String [] args) throws IOException{
   FileReader filein = new FileReader("copyread.txt"); 
                        // apre il file
   int next;
   char nextc;
   do{
      next = filein.read();  // legge il prossimo carattere
      if (next != -1){    // se non e' finito il file
          nextc = (char) next; 
          System.out.print(nextc);  // stampa il carattere
      }
   } while (next != -1);
   filein.close();      //chiude il file
   System.out.println();
 }
}

 



Scrittura di caratteri su file

Per scrivere dati su di un file, occorre:
  • creare un oggetto della classe FileWriter, passando il nome del file al costruttore; 

  •  
  • utilizzare il metodo public void write(int c) per scrivere i caratteri;

  •  
  • chiudere il file con il metodo close().
Anche questi metodi possono lanciare una IOException.
 
CopyWrite.java: scrive in un file (copywrite.txt) una stringa fornita dall'utente, un carattere alla volta.
 
import java.io.*;

public class CopyWrite {
   public static void main(String[] args) throws IOException {
      ConsoleReader console = new ConsoleReader(System.in);
                    // leggo una stringa da tastiera 
      FileWriter fileout = new FileWriter("copywrite.txt");
      System.out.print("Scrivi una stringa: ");
      String str = console.readLine();
      for (int i = 0; i < str.length(); i++)
                // scrivo i caratteri delle stringa nel file
          fileout.write(str.charAt(i));
      fileout.close();
     }
 }
 

 



I/O bufferizzato e formattato

Le classi FileReader e FileWriter forniscono i metodi basici per leggere o scrivere caratteri su file. Non è conveniente usarle direttamente nei programmi perché:
  • rendono un programma inefficiente, visto che ogni operazione di I/O (read o write) richiede un accesso al file;

  •  
  • non permettono di leggere/scrivere dati più complessi come stringhe e numeri.
Altre classi di Java forniscono funzionalità di I/O più complesse, in particolare
  • BufferedReader e BufferedWriter usano un buffer (memoria tampone) per memorizzare temporaneamente i caratteri da leggere/scrivere, in modo da ridurre il numero di accessi al file;

  •  
  • PrintWriter fornisce i metodi print e println, che permettono di scrivere qualunque dato Java, convertendolo automaticamente in stringa.
Gli oggetti di queste classi sono dei wrappers: incapsulano gli oggetti delle classi FileReader e FileWriter estendendone le funzionalità.



Scrittura di dati su file

CopyPrintWrite.java: scrive alcuni dati in un file (copyprintwrite.txt). 
Si noti come un oggetto FileWriter viene passato al costruttore di BufferedWriter, e a sua volta il nuovo oggetto viene passato al costruttore di PrintWriter
 
import java.io.*;

public class CopyPrintWrite {
  public static void main(String[] args) throws IOException {

                // creo un oggetto FileWriter...
   FileWriter fileout = new FileWriter("copyprintwrite.txt");
                // ... che incapsulo in un BufferedWriter...
   BufferedWriter filebuf = new BufferedWriter(fileout);
                // ... che incapsulo in un PrintWriter
   PrintWriter printout = new PrintWriter(filebuf);

   printout.println("Scrivo nel file copyprintwrite.txt:");
   printout.print("Un numero: ");
   printout.println(Math.PI);
   printout.print("Un oggetto: ");
   printout.println(new java.awt.Rectangle(10,15,20,30));
   printout.println("Ho finito.");
   printout.close();
  }
}
 

 



Lettura di dati da file

La classe BufferedReader fornisce il metodo readLine() che legge una stringa, ma non ha metodi per leggere, ad esempio, interi o double

CopyBufferedRead.java: stampa su video del contenuto di un file (copyread.txt) (si confronti con CopyRead.java)
 

import java.io.*;

public class CopyBufferedRead {
  public static void main(String [] args) throws IOException{
    BufferedReader filebuf = 
         new BufferedReader(new FileReader("copyread.txt"));
    String nextStr;
    nextStr = filebuf.readLine();
    while (nextStr != null){
       System.out.println(nextStr);
       nextStr = filebuf.readLine();
    } 
    filebuf.close(); 
  }
}

Si può convertire una stringa in un numero usando i metodi statici Integer.parseInt e Double.parseDouble. Si veda come questi metodi sono usati nella classe ConsoleReader, per realizzare i metodi readInt e readDouble. 




Esempi con gestione delle eccezioni di I/O

Vediamo alcuni programmi che copiano il contenuto del file inp.txt nel file out.txt, ma che gestiscono le eccezioni in modo via via più raffinato.