Fisica Computazionale I - 51 OPERAZIONI DI INPUT/OUTPUT Le operazioni di input/output sono estremamente complesse perche' implicano una sostanziale interazione con l'ambiente esterno a Java, cioe' con il sistema operativo. Poiche' Java ha l'ambizione di essere un linguaggio universale si devono prevedere tutte le possibili situazioni ambientali, sia hardware che software. questo si traduce nel fatto che nelle operazioni di lettura e scrittura intervengono moltissime classi, create appunto per rendere possibile la gestione di tutte o quasi tutte le situazioni operative.
Fisica Computazionale I - 52 Le classi rilevanti sono contenute nel package java.io: dovremo quindi importarle premettendo ai nostri programmi l'istruzione import java.io.*; Il concetto fondamentale alla base delle operazioni di input/output e' quello di stream. Uno stream e' un flusso di dati che, almeno concettualmente, parte da un oggetto che scrive ed arriva ad un oggetto che legge.
Fisica Computazionale I - 53 Sulle base di questo concetto Java fornisce una gerarchia di classi, di cui ne citiamo alcune: InputStream/OutputStream classi astratte che definiscono le funzionalita' fondamentali per leggere o scrivere una sequenza di bytes. Reader/Writer classi astratte che definiscono le funzionalita' fondamentali per leggere o scrivere una sequenza di caratteri, con supporto Unicode. DataInputStream/DataOutputStream classi piu' sofisticate che possono leggere o scrivere dati di tipo semplice (primitivi o Strings).
Fisica Computazionale I - 54 BufferedInputStream/BufferedOutputStream/BufferedReader /BufferedWriter Classi che sono in grado di effettuare 'buffering' del flusso di dati per aumentare l'efficienza PrintWriter Stream specializzata per la stampa di caratteri FileInputStream/FileOutputStream/FileReader/FileWriter implementazioni delle classi astratte InputStream, OutputStream, Reader e Writer per leggere o scrivere files.
Fisica Computazionale I - 55 Possiamo ora comprendere finalmente il vero significato dell'istruzione System.out.println(...) che finora abbiamo usato senza comprenderla. Il metodo statico System.out restituisce un oggetto PrintStream; println(..) e' uno dei metodi di PrintStream; quindi il tutto e' equivalente a scrivere: PrintStream out = System.out; out.println(.....);
Fisica Computazionale I - 56 System.out scrive sull'unita' di default dell'uscita (il cosidetto 'standard output' del linguaggio Unix), cioe' la finestra di terminale da cui abbiamo lanciato il programma. Si noti che println(...) e' un metodo sovraccarico, cioe' definito piu' volte, per accettare come argomento qualunque tipo primitivo, e oggetti String. La PrintStream possiede anche metodi print(): a differenza di println, il dato in output e' inviato senza essere preceduto da un 'a capo'.
Fisica Computazionale I - 57 La classe System definisce anche un modo per leggere dallo 'standard input'; il metodo System.in restituisce una InputStream i cui metodi ci consentono di leggere, se lo desideriamo, dalla finestra di comando. Infine, con il metodo System.err essa definisce un'altra PrintStream di output, il cosidetto 'standard error'. Per default esso coincide con lo standard output, ma, la differenziazione puo' consentire di scrivere in luoghi diversi output normale e messaggi di errore, usando opportunamente System.out.... e System.err.....
Fisica Computazionale I - 58 Lettura di un file di testo Possiamo ora applicare quanto visto in precedenza, cominciano ad imparare come poter leggere un file di testo. Scriviamo il seguente codice: String nomeFile = "prova.txt"; File f = new File(nomeFile); try{ FileReader fr = new FileReader(f); BufferedReader in = new BufferedReader(fr); String line; while((line = in.readLine()) != null) System.out.println(line); }catch(Exception ex) { System.out.println("File inesistente oppure non leggibile"); }
Fisica Computazionale I - 59 Definiamo una istanza della classe File associandola al file che vogliamo leggere attraverso il nome dello stesso. La classe FileReader fornisce il supporto necessario per leggere i caratteri, utilizzando le codifiche di default; la classe BufferedReader si preoccupa di gestire le operazioni di lettura (effettuate da FileReader) fornendoci direttamente un metodo per leggere una riga alla volta. Quindi, attraverso il blocco while(....) leggiamo tutte le righe in sequenza e il contenuto e' riportato sullo standard output; quando le righe da leggere sono finite il metodo readLine() restituisce null e possiamo interrompere la lettura.
Fisica Computazionale I La classe FileReader puo' generare un'eccezione (FileNotFoundException) se il file non esiste, oppure se non e' un file leggibile (ad esempio e' il nome di una directory, oppure per altri motivi. E' quindi obbligatorio, o comunque sensato, gestire questa eccezione con try/catch. L'esempio completo si trova tra gli esercizi del capitolo5, con il nome Leggi.java.
Fisica Computazionale I Il nome del file che viene fornito al costruttore della classe File puo' contenere il path dell'indirizzo del file ed essere relativo o assoluto. E' in grado di comprendere sia la nomenclatura in stile Unix che quella Windows. Un indirizzo assoluto e' riconosciuto dal prefisso rilevante dell'ambiente in cui ci si trova. Quindi se siamo in ambiente Windows, un nome tipo C:\esercizi\prova.txt viene riconosciuto correttamente come un indirizzo assoluto, e analogamente, in ambiente Unix, /home/rossi/esercizi/prova.txt.
Fisica Computazionale I Viceversa un path relativo viene interpretato a partire dalla directory corrente. Per esempio il nome esercizi/prova.txt (o esercizi\prova.txt in ambiente Windows)conduce al file prova.txt nella sotto-directory esercizi della directory corrente. La classe File ha una serie di metodi che ci consentono di gestire sostanzialmente tutte le situazioni (consultare la documentazione !).
Fisica Computazionale I Naturalmente le operazioni su files sono possibili solo se il programma possiede i privilegi per poterle eseguire. Chi conosce l'ambiente Unix comprende cosa questo significa: il programma porta con se' i privilegi dello user cui appartiene. In ambiente Windows98 non vi sono protezioni, quindi e' possibile fare un programma che distrugge tutti i files di sistema senza alcun problema! Windows2000 e Windows XP hanno introdotto uno schema di gestione degli users e di protezione del file system abbastanza analogo a quello Unix.
Fisica Computazionale I Scrittura di un file import java.io.*; /* Scrive su un file */ public class Scrivi{ public static void main(String args[]){ try{ File out = new File("prova-out.txt"); FileWriter fw = new FileWriter(out); PrintWriter pw = new PrintWriter(fw); pw.println("Sto scrivendo sul file prova.txt"); fw.close(); }catch(Exception xx){} }
Fisica Computazionale I Si noti che questo programma crea un nuovo file prova-out.txt se esso esiste gia' viene cancellato e ne viene creato uno nuovo. Se invece si desidera continuare a scrivere su un file gia' esistente occorre usare un altro costruttore della classe FileWriter FileWriter(File f, boolean append) se la variabile boolean append e' true allora il programma continua a scrivere sul file esistente
Fisica Computazionale I File out = new File("prova.txt"); FileWriter fw = new FileWriter(out, true); PrintWriter pw = new PrintWriter(fw); pw.println("Sto scrivendo sul file prova.txt"); fw.close();
Fisica Computazionale I Gli esempi precedenti sono relativi a files di testo. Quando dobbiamo leggere (o scrivere) un file qualunque dovremo usare una tecnica diversa, illustrata nell'esempio che segue, in cui si effettua la copia di un file, in modalita' binaria.
Fisica Computazionale I public class CopyFile{ public static void main(String[] argv) { String input = argv[0]; String output = argv[1]; try { File outFile = new File(output); File inFile = new File(input); FileOutputStream outf = new FileOutputStream(outFile); FileInputStream in = new FileInputStream(inFile); BufferedReader inb = new BufferedReader(new InputStreamReader(in, "8859_1") ); byte [] data = new byte [ 64*1024 ]; for(int read; (read = in.read( data )) > -1; ) {outf.write( data, 0, read );} } catch ( Exception e ) {e.printStackTrace(); // I/O error} }