File e Stream In Java Prof. Francesco Accarino IIS Sesto San Giovanni Via Leopardi 132 Lettura da e scrittura su file Java fornisce operazioni di input/output tramite le classi del package java.io. La struttura è indipendente dalla piattaforma. Le operazioni si basano sul concetto di flusso. Un flusso (stream) è una sequenza ordinata di dati che ha una sorgente e una destinazione. L’ordine della sequenza è importante: possiamo pensare a un nastro che viene inciso o riprodotto in un ordine prefissato, un dato dopo l’altro. Appunti di Informatica Prof. Accarino 2 Lettura da e scrittura su file Tastiera File Gli Stream sono un’astrazione messa a disposizione dai moderni Sistemi Operativi che permettono alle applicazioni di prelevare informazioni da varie sorgenti o inviare informazioni a varie destinazioni. Memoria Rete Monitor File Memoria Rete Appunti di Informatica Prof. Accarino 3 Lettura da e scrittura su file L’uso degli stream maschera la specifica natura fisica di una sorgente o una destinazione. Si possono trattare oggetti differenti allo stesso modo, ma anche oggetti simili in modi differenti. In particolare, esistono due modi principali: modalità testo(per esempio, per file di testo o per l’output a console video): immediato per l’utente modalità binaria(per dati elaborati): si leggono e scrivono byte, risultati non immediati per l’utente Appunti di Informatica Prof. Accarino 4 Lettura da e scrittura su file In modalità testo i dati manipolati sono in forme simili al tipo char di Java. (Caratteri ASCII) In modalità binaria i dati manipolati sono byte. Le classi coinvolte terminano in -Reader, -Writer Le tipiche classi coinvolte si chiamano gestori di flussi e hanno il prefisso–(Input/Output)Stream Per entrambi i casi abbiamo già visto le applicazioni riguardanti lettura da tastiera e scrittura a video (con conversioni da byte a stringhe) Appunti di Informatica Prof. Accarino 5 Il file system Il tipo File può contenere un riferimento a un file fisico del sistema. Il riferimento è creato da un costruttore con un parametro di tipo stringa che rappresenta il path-name del file. File x = new File(“temp.tmp”); associa il file temp.tmp all’oggetto File di nome x. La classe dispone di utili metodi boolean: exists(), canRead(), canWrite(), isFile(), isDirectory() Appunti di Informatica Prof. Accarino 6 Il file system La classe File ha poi altri metodi utili, come: length(): ritorna il valore in byte list(): se invocato su una cartella, ritorna i nomi dei file in un array di stringhe setReadable(),setWritable(), setReadOnly(): imposta diritti di lettura/scrittura createNewFile(),mkdir(): crea file/cartella delete(): cancella file/cartella getParent(): ritorna la cartella madre (./..) Appunti di Informatica Prof. Accarino 7 Lettura/Scrittura verso file di testo Per gestire testi, Java fornisce due gerarchie, in lettura (Reader) e in scrittura (Writer). Reader e Writer sono due classi astratte che servono a definire i metodi di base per lettura e scrittura da file. Sono compresi: flush()/close(): scarica/scarica+chiude flusso read()/write(): leggi/scrivi pacchetti di char L’implementazione specifica di questi metodi dipende dall’estensione che si usa. Appunti di Informatica Prof. Accarino 8 Gerarchia di Reader Appunti di Informatica Prof. Accarino 9 Gerarchia di Writer Appunti di Informatica Prof. Accarino 10 Scrittura su file di testo Per aprire un file di testo, tipicamente si crea un oggetto di tipo FileWriter. (Stream Di Scrittura) La classe FileWriter ha due costruttori che come parametro hanno il riferimento al file espresso o come un oggetto di tipo File o direttamente dalla stringa che contiene il path-name completo del file su cui scrivere). File f= new File(“Prova.txt”); FileWriter fw= new FileWriter(f); FileWriter fw= new FileWriter((“Prova.txt”); A sua volta, FileWriter è incapsulato in un oggetto di tipo PrintWriter. Per utilizzare metodi più performanti come println() con i quale scriviamo sul file una intera linea PrintWriter pw = new PrintWriter(fw); pw.println(“riga di testo”); Appunti di Informatica Prof. Accarino 11 Scrittura su file di testo La classe PrintWriter è contenuta nel package java.io e serve a fornire gli stessi metodi della classe PrintStream(visti ad esempio per la sua istanza System.out). Solo, invece di stampare testo a video, lo scrivono sul file associato. Quindi si possono utilizzare i metodi print() e println() di PrintWriter per scrivere caratteri in un file aperto con successo. Appunti di Informatica Prof. Accarino 12 Il metodo printf Print e println sono metodi versatili: sono in grado di stampare tipi diversi di dato. PrintWriter (e PrintStream) hanno anche un metodo di stampa formattata: printf. N Notare che i metodi di PrintWriter non sollevano eccezioni Appunti di Informatica Prof. Accarino 13 Esempio di scrittura file di testo publicstatic voidstampaCostanti() { f = new File(“costanti.txt”); if ( f.exists() ) { System.out.println(“costanti.txt esiste!”); return; } String[] nomi = { “Pi greco”, “Nepero” }; double[] valori = { Math.PI, Math.E }; FileWriter fw = new FileWriter(f); PrintWriter pw = new PrintWriter(fw); for ( int i = 0; i<nomi.length; i++) { pw.printf(“%s è:%10.6f”,nomi[i],valori[i]); } close(f); } Appunti di Informatica Prof. Accarino 14 Lettura da file di testo Analogamente alla scrittura, per leggere da file si creerà invece un oggetto FileReader. Anche in questo caso il costruttore può ricevere un parametro File o stringa. Tuttavia, un FileReader dovrebbe leggere un carattere alla volta, quindi di solito viene incapsulato in un oggetto BufferedReader: BufferedReader r = new BufferedReader(new FileReader(“a.dat”)); Appunti di Informatica Prof. Accarino 15 Esempio di lettura file di testo publicstatic voidstampaIlFile() { f = new File(“a.txt”); if ( !f.exists() ) { System.out.println(“a.txt non esiste!”); return; } FileReader fr = new FileReader(f); BufferedReader re = new BufferedReader(fr); String linea = re.readLine(); while (linea != null){ System.out.println(linea); linea = re.readLine(); } close(f); } Appunti di Informatica Prof. Accarino 16 Scrittura file in modalità append Di default il contenuto di un file viene sovrascritto. Se invece volessimo inserire nuovi caratteri in fondo a un file preesistente esistono ulteriori costruttori: FileWriter(File, boolean) FileWriter(String, boolean) che chiedono se vogliamo fare un append (in tal caso la variabile boolean = true). Appunti di Informatica Prof. Accarino 17 File Binari Le classi che Java mette a disposizione per leggere e scrivere file binari, sono rispettivamente : java.io.FileInputStream java.io.FileOutputStream. La classe FileInputStream è l’analogo della classe FileReader che viene utilizzata per leggere i dati da un file di testo. La classe FileOutputStream è l’analogo della classe FileWriter che viene utilizzata per scrivere i dati in un file di testo. Creazione dell’istanza Per ottenere un’istanza della classe FileInputStream, che ci permette di aprire un file binario in modalità lettura, possiamo utilizzare uno dei seguenti costruttori della classe: FileInputStream(File file) che riceve in ingresso un’istanza della classe File. FileInputStream(FileDescriptor fdObj) che riceve in ingresso un’istanza della classe FileDescriptor. che riceve in ingresso una stringa cheFileInputStream(String name) rappresenta il path del file. Tutti e tre i costruttori possono sollevare un’eccezione FileNotFoundException qualora il file non viene trovato oppure non può essere aperto. Gerarchia delle classi Appunti di Informatica Prof. Accarino 20 Metodi di letura Per leggere un byte si utilizza il metodo read(), che restituisce il byte sottoforma di numero intero compreso tra 0 e 255. Il metodo read può sollevare un’eccezione, IOException, se per qualche motivo il byte corrente non può essere letto. Esistono 3 versioni differenti del metodo read: int read() che permette di leggere un byte a partire dalla posizione corrente. int read(byte[ ] b) che permette di leggere b.length byte dal file. I byte letti vengono memorizzati nel byte array b. int read(byte[] b, int off, int len) che permette di leggere len byte dal file a partire dalla posizione off. I byte letti vengono memorizzati nel byte array b. Quando la fine del file viene raggiunta (EOF End of file), il metodo read restituisce -1. Al termine della lettura il file può essere chiuso invocando il metodo close(). Lettura di un file binario public static void read() { int c; try { FileInputStream fis = new FileInputStream(new File("prova.bin")); c = fis.read(); while (c != -1) { c = fis.read(); } fis.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } FileOutputStream Per creare un’istanza della classe FileOutputStream, che ci permette di aprire un file binario in modalità scrittura, possiamo utilizzare uno dei seguenti costruttori della classe: FileOutputStream(File file) che riceve in ingresso un’istanza della classe File. FileOutputStream(File file, boolean append) che riceve in ingresso un’istanza della classe File ed apre il file in modalità append. FileOutputStream(FileDescriptor fdObj) che riceve in ingresso un’istanza della classe FileDescriptor. FileOutputStream(String name) che riceve in ingresso una stringa che rappresenta il path del file. FileOutputStream(String name, boolean append) che riceve in ingresso una stringa che rappresenta il path del file ed apre il file in modalità append. Anche in questo caso può essere sollevata un’eccezione FileNotFoundException qualora il file non viene trovato oppure non può essere aperto in modalità scrittura. Scrittura di un file binario Per scrivere un byte all’interno del file occorre invocare il metodo write(int c) che riceve in ingresso un valore intero rappresentante il gruppo degli 8 bit meno significativi del byte. Esistono 3 versioni differenti del metodo write: void write(int b) che permette di scrivere il byte b nel file. void write(byte[] b) che permette di scrivere b.length byte nel file. void write(byte[] b, int off, int len) che permette di scrivere nel file len byte prelevati dal byte array b a partire dalla posizione off. Al termine delle operazioni di scrittura, il file deve essere chiuso invocando il metodo close(), per assicurarsi che il contenuto dei buffer di sistema sia effettivamente trasferito sul file. Scrittura di un file binario public static void write() { int i = 1; try{ FileOutputStream fos = new FileOutputStream(new File("test.bin")); while (i<10) { fos.write(i); i++; } fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } Copia di un file binario public static void copia(){ int c; try{ FileOutputStream fos = new FileOutputStream(new File("out.jpg")); FileInputStream fis = new FileInputStream(new File("in.jpg")); c = fis.read(); while (c != -1) { c = fis.read(); fos.write(c); } fis.close(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e){ e.printStackTrace(); } } Lettura e scrittura di tipi primitivi Le classi DataInputStream e DataOutputStream Forniscono metodi per leggere e scrivere dati primitivi I dati vengono codificati in formato binario e non sono leggibili come testi. DataOutputStream dout = new DataOutputStream( new FileOutputStream("prova.dat")); dout.writeInt(250); dout.writeDouble(3.14); dout.writeChar('a'); dout.close(); DataInputStream din = new DataInputStream( new FileInputStream("prova.dat")); System.out.println(din.readInt()); System.out.println(din.readDouble()); System.out.println(din.readChar()); Appunti di Informatica Prof. Accarino 27 I/O di oggetti In Java è possibile leggere o scrivere qualunque oggetto con ObjectInputStream e ObjectOutputStream. ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("impiegati.dat")); Impiegato rossi = new Impiegato(......); out.writeObject(rossi); ObjectInputStream in = new ObjectInputStream( new FileInputStream("impiegati.dat")); Impiegato imp = (Impiegato)in.readObject(); Appunti di Informatica Prof. Accarino 28 I/O di oggetti E' possibile leggere o scrivere un oggetto solo se questo appartiene ad una classe che implementa l'interfaccia Serializable. class Impiegato implements Serializable(...) L'interfaccia Serializable non contiene nessun metodo. Deve essere specificata solo per motivi di sicurezza. Quando si legge un oggetto, Java controlla che la definizione della classe non sia cambiata da quando l'oggetto era stato scritto. Appunti di Informatica Prof. Accarino 29 I/O di oggetti Cosa succede se si scrive un oggetto che contiene riferimenti ad altri oggetti? Vengono scritti anche tutti gli altri oggetti raggiungibili. impiegato Mansione imp DataAssu Data Se si esegue out.writeObject(imp) vengono scritti in out sia l'oggetto Impiegato che l'oggetto Data. Appunti di Informatica Prof. Accarino 30