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:
- Flussi
di caratteri: utilizzati per l’I/O basato sul testo e, in
particolare, per trasmettere caratteri Unicode a 16 bit. I dati
vengono scritti e letti in pezzi da 16 bit e quindi sono
compatibili col tipo char di Java. Tali flussi vengono chiamati
reader e writer e vengono gestiti dalle due classi astratte
Reader e Writer del package Java.io.
- Flussi
di byte: sono utilizzati per leggere e scrivere dati non
testuali, dividendoli in pezzi da 8 bit. Vengono chiamati flussi
di ingresso e di uscita e sono gestiti dalle classi astratte InputStream e
OutputStram. Il metodo write dei flussi di byte
scrive solo byte, quindi per scrivere un intero bisogna estrarne
i suoi byte con String.valueOf(i).getBytes(). Gli oggetti
PrintStream semplificano le cose, consentendo di scrivere
qualsiasi tipo.
La classe Java.lang.System definisce tre flussi di byte
standard:
- System.out: è un oggetto del tipo PrintStream,
rappresenta
l’output di default del sistema.
- System.err: è un oggetto del tipo PrintStream,
rappresenta
l’output degli errori di default.
- System.in: è di tipo InputStram e in genere
rappresenta la
tastiera. Non può essere usato al posto di un Reader.
Per convertire flussi di byte in flussi di caratteri si possono
utilizzare i seguenti due oggetti:
- InputStreamReader(InputStream in, String charsetName)
- OutputStreamWriter(OutputStream out, String charsetName)
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:
- US-ASCII: è ASCII a 7 bit.
- ISO-8859-1: è l’alfabeto ISO Latin no.1.
- UTF-8: è
Unicode a 8 bit con ordinamento Big Endian.
- UTF-16:
è Unicode a 16 bit e in input supporta pure l’ordinamento Little Endian.
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.
- BufferedInputStream(InputStream in)
- BufferedOutputStream(OutputStream out)
- BufferedReader(Reader in)
- BufferedWriter(Writer out)
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();
...
- Creiamo
un FileReader (a partire dal nome del file o dall’oggetto File)
- Creiamo
il BufferedReader passandogli il FileReader
- Usiamo
questo oggetto per leggere le linee del file
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();
}
}