Input-Output
16 Maggio 2006
Nei programmi sviluppati sino ad oggi abbiamo usato
*
output su schermo (con System.out.print e System.out.print)
*
input da tastiera (con la classe Input)
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)
I dati possono essere memorizzati in un file in formato
•testo (sequenza di caratteri, leggibile da esseri umani)
•binario (sequenza di byte)
•Vedremo solo il primo caso (il secondo comunque e’ piu’ efficiente)
•Per esempio formato testo per il numero intero 12354,
`1` `2``3``5``4` un carattere alla volta
Ricordiamo che
• Tipo Primitivo: char
• Contiene un solo carattere tra apici singoli
‘a’ carattere
“a” stringa
Il metodo charAt (int i) permette di leggere il
carattere in posizione i di una stringa
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.
I file di testo possono essere aperti, esaminati e modificati
usando normali editor (es. emacs).
Lettura di singoli caratteri da file
Per leggere dati (un carattere alla volta) da un file, occorre:
*
ricordarsi di importare il pacchetto java.io;
import java.io.*;
•creare un oggetto della classe FileReader, passando il nome
del file al costruttore;
FileReader filein = new FileReader("dati.txt");
Per leggere
• utilizzare il metodo public int read() della classe
FileReader per leggere i caratteri;
int next = filein.read();
• Il metodo read() restituisce un intero che può essere:
•
-1 se si è arrivati alla fine del file;
•
un intero che rappresenta il codice di un carattere UNICODE.
• Tipicamente, si controlla se il numero letto è diverso da -1 e in questo
caso si trasforma l'intero in un char usando l'operatore di cast.
char c = (char) next;
Attenzione
•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.
•Quando si termina di leggere o scrivere
bisogna “chiudere” il file con il metodo public
filein.close();
void close().
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.
•Importante: Cosa succede se non dichiariamo che main può
lanciare IOException?
•Le eccezioni controllate devono essere riportate
nell’intestazione del metodo tramite throws?
import java.io.*;
public class CopyRead {
public static void main(String [] args) throws IOException {
// apre il file in lettura
FileReader filein = new FileReader("copyread.txt");
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
*chiudere
il metodo public
void write(int c)
per scrivere i caratteri;
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 {
// apre il file copywrite.txt in scrittura
FileWriter fileout = new FileWriter("copywrite.txt");
System.out.print("Scrivi una stringa: ");
// legge una stringa da tastiera
String str = Input.readLine();
// un ciclo scrive ogni carattere delle stringa nel file
for (int i = 0; i < str.length(); i++)
fileout.write(str.charAt(i));
fileout.close();
}
}
// chiude il file
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 di singolo carattere) richiede un accesso al file;
•non permettono di leggere/scrivere direttamente dati più
complessi come stringhe e numeri.
Altre classi di Java forniscono funzionalità di I/O più avanzate,
ne vedremo alcune
Altre classi di Java forniscono funzionalità di I/O più avanzate,
in particolare
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;
•BufferedReader
fornisce i metodi print e println, che permettono
di scrivere qualunque dato Java, convertendolo automaticamente in
stringa.
•PrintWriter
Gli oggetti di queste classi sono dei wrappers: incapsulano gli
oggetti delle classi FileReader e FileWriter estendendone
le funzionalità.
Esempio
• Vogliamo scrivere direttamente in un file un
numero, un oggetto (il suo stato interno), o
una stringa
FileWriter fileout = new FileWriter("copyprintwrite.t
xt");
// ... che incapsulo in un PrintWriter
PrintWriter printout = new PrintWriter(fileout);
PrintWriter
• Ha metodi print e println per
stampare numeri oggetti e stringhe
• Allo stesso tempo questi metodi convertono
tutto in una stringa, la stringa viene poi
scomposta in caratteri e mandata all’oggetto
FileWriter corrispondente
FileWriter fileout = new FileWriter("copyprintwrite.txt");
// ... che incapsulo in un PrintWriter
PrintWriter printout = new PrintWriter(fileout);
printout.println(115); //numero
printout.println(“Hello”); //Una stringa
printout.println(new Studente(“pippo”); //Un oggetto
•Nel caso dell’oggetto viene usato ToString(),
alla fine nel file e’ memorizzata la Stringa un
carattere alla volta
Lettura di dati da file
•Conviene usare la classe BufferedReader
fornisce il metodo readLine() che legge una riga (String)
•In pratica continua a leggere nel file di input dell’oggetto
lettore relativo tramite read() fino a costruire una stringa
•Quando i dati sono terminati readLine() restituisce null
•non ha metodi per leggere, ad esempio, interi o double.
import java.io.*;
public class CopyBufferedRead {
public static void main(String [] args) throws IOException {
// incapsula in BufferedReader un file aperto in lettura
BufferedReader filebuf =
new BufferedReader(new FileReader("copyread.txt"));
String nextStr;
// legge una riga del file
nextStr = filebuf.readLine();
while (nextStr != null){
System.out.println(nextStr); // visualizza la riga
nextStr = filebuf.readLine(); // legge la prossima riga
}
filebuf.close(); // chiude il file
}
}
•La differenza rispetto al programma visto prima e’ che legge stringhe,
non caratteri
Primo esercizio
• Scrivere programma che copia il file "inp.txt" nel file
"out.txt” un carattere alla volta
• Modificarlo in modo che legga una linea alla volta
Una soluzione
import java.io.*;
public class CopiaFile {
public static void main(String [] args) throws IOException {
// apre il file in lettura
FileReader filein=new FileReader("inp.txt");
FileWriter fileout = new FileWriter("out.txt");
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
fileout.write(nextc);
}
} while (next != -1);
filein.close();
fileout.close(); //chiude il file
System.out.println();}}
Secondo Esercizio
• Dato un file integers.txt che contiene una sequenza di
interi uno per linea
• Scrivere un programma che calcola la somma degli interi
contenuti nel file
• Si può convertire una stringa in un numero usando i metodi
statici Integer.parseInt e Double.parseDouble.
public class SommaInteri {
public static void main(String[] args){
String inputFileName = "./integers.txt";
String line = null;
try {
BufferedReader in = new BufferedReader(new FileReader(inputFileName));
line = in.readLine();
int somma =0;
while(line!=null){
// se non e' possibile convertire la stringa line in intero
// viene lanciata un'eccezione NumberFormatException
// (catturata sotto con catch apposito)
int numero = Integer.parseInt(line);
somma += numero;
line = in.readLine();
}
in.close();
System.out.println(" la somma e' "+ somma);
} catch(FileNotFoundException e) {
System.out.println(inputFileName+" FileNotFound");
} catch(NumberFormatException e) {
System.out.println(" linea non corretta: --> "+line+" <--");
} catch(IOException e) {
System.out.println(" IOException "+e);
}}}
Eccezioni
• Si possono verificare varie eccezioni
• FileNotFoundException se non si trova il
file
se la stringa letta nel
file non si puo’ convertire in intero
• NumberFormaxception
Quindi
• Sarebbe meglio gestire le eccezioni invece che
propagarle
• Modificare il programma SommaInteri in modo che quando si verifica
un'eccezione venga comunque stampata la somma calcolata fino a quel
momento.
• 2. Modificare il programma [SommaInteri] in modo che quando si
verifica un'eccezione di tipo NumberFormatException dovuta ad una
linea del file non convertibile in intero si scarti tale riga ma si prosegua
con il calcolo della somma.
Esercizio Aggiuntivo
Scrivere un programma CatFiles contenente il metodo statico
public static void concat(String [] inFiles, String
outFile)
che concatena il contenuto dei files il cui nome è nell'array inFiles nel file il
cui nome è outFile.
Se una delle stringhe dell'array inFiles non corrisponde ad un file reale, la
stringa viene ignorata e si passa alla prossima. Eventuali altre eccezioni di I/O
devono essere catturate dal metodo, che in questo caso deve lanciare
una IllegalArgumentException.
Per testare il programma, scrivere una semplice classe TestCatFiles che
nel main invoca il metodo in questione dopo aver chiesto i nomi dei file all'utente.
Controllare che l'effetto sia quello desiderato.