Java input/output: Come leggere e scrivere un file in Java

Compatibile con Java 6
Spesso le applicazioni hanno bisogno di utilizzare dati letti da fonti esterne o di inviare date all’esterno: un flusso (o stream) è una sequenza ordinata di dati che hanno una sorgente o una destinazione. Distinguiamo due tipi di flussi:
La classe Java.lang.System definisce tre flussi di byte standard:
Per convertire flussi di byte in flussi di caratteri si possono utilizzare i seguenti due oggetti:
Entrambi questi oggetti usano la codifica di default del sistema, ma il costruttore consente di passare una stringa che descrive la codifica che si vuole utilizzare:

I FLUSSI FILTER

Questi flussi consentono di concatenare flussi per produrre dei composti maggiormente utili: delegano le azioni di input e output al flusso al quale sono collegati. Ad esempio, un filtro che converte una stringa in maiuscolo deve estendere FilterReader e deve definire il metodo read:
public int read() throws IOException {
int c = super.read();
if(c==-1)
return c;
else
return Character.toUpperCase((Char) c);
}

I FLUSSI BUFFERED

Questi flussi memorizzano i loro dati in un buffer, in modo da evitare che ogni operazione di input e output si rivolga direttamente al flusso successivo (vengono spesso usati insieme ai flussi File). Richiedono un riferimento al flusso incapsulato e, opzionalmente, la dimensione del buffer da utilizzare. Quando si invoca read su un flusso buffered, questo lo invoca più volte sul flusso sorgente cercando di riempire il più possibile il buffer: le successive invocazioni di read prelevano i dati dal buffer finché ce ne sono. Analogamente, write sul flusso sorgente sarà chiamato solo quando il buffer è pieno.
I flussi di caratteri presentano metodi utili aggiuntivi. BufferedReader offre il metodo readLine() che restituisce una linea di testo sottoforma di String, riconoscendo la fine della linea grazie al separatore di linea (\n, \r oppure \r\n). BufferedWriter offre invece il metodo newLine() che permette di scrivere nel flusso un separatore di linea.
 

I FLUSSI FILE

Questi flussi consentono di trattare un File come se fosse un flusso di input o di output. Sono quattro (FileInputStream, FileOutputStream, FileReader, FileWriter) e il loro costruttore richiede o una stringa contenente il nome del file, o un oggetto Java.io.File o un oggetto FileDescriptor.
Se il file non esiste, i flussi di output lo creano, se esiste e si passa true al costruttore, i nuovi dati vengono appesi al file. Il metodo flush dei flussi di output garantisce che il buffer venga svuotato nel filesystem, ma non che i dati vengano inviati al disco, perché lo stesso filesystem potrebbe avere un buffer.

LA CLASSE FILE

Fa parte del package Java.io e astrae il concetto di file generico: anche una directory è un file, cioè un file che contiene altri file. Il costruttore principale richiede come parametro una stringa, che rappresenta il percorso del file. Un secondo costruttore accetta il percorso della directory superiore al file e il nome del file, che concatenati compongono il percorso del file. Il percorso del file è composto da parti distinte (cioè i nomi di file e directory) separate da un carattere che dipende dal sistema operativo e che può essere ottenuto dall’invocazione di File.separator.
Un’altra interfaccia utile è FilenameFilter, che permette di filtrare i file presenti in un elenco. Per implementarla basta definire l’unico metodo accept che restituisce true se il file passato per parametro fa parte dell’output filtrato.
 

ELENCO FILTRATO DEI FILE PRESENTI IN UNA DIRECTORY

/**
 * Filters .txt files
 */

public class FileFilterAll implements java.io.FilenameFilter {
    public boolean accept(File dir, String name) {
           if (name == null) return false;
           if (name.startsWith("//")) return false;
           if (name.endsWith(".txt")) return true;
           return false;
    }
}


/**
 * This class implements a loader for files
 */

public class Loader {
      private File directory;
 
      public List<String> load(String path) {
            directory = new File(path);
            return load(directory.list(new FileFilterAll()));
}
...
Il metodo load prende come parametro il percorso di una directory e restituisce una lista di nomi dei file in essa presenti, filtrati secondo l’oggetto FileFilterAll, che considera solo i file di testo.
 

LEGGERE IL CONTENUTO DI UN FILE

public void readFile(File file) throws IOException{
      BufferedReader lettore = new BufferedReader(new FileReader(file));
     
//per ogni riga ne analizziamo il contenuto
        while(lettore.ready()){
                  String riga = lettore.readLine();
                    ...

SCRIVERE IL CONTENUTO DI UN FILE

public class MyFileWriter {

   
//flusso su cui scrivere
      private BufferedWriter out = null;
 
   
//costruttore
      public MyFileWriter(String fileName) throws IOException{
            //creo il flusso, cancellando il contenuto esistente del file
            out = new BufferedWriter(new FileWriter(fileName));
      }
 
      public void writeMyFile() throws IOException{
            out.write("MY NAME: FABRIZIO");
            out.newLine();
            out.write("ENJOY");
            out.newLine();
            out.flush();
      }
}