Streams e Input/output
Scansione e Formattazione di un testo Streams Testuali e Binarie Streams di dati primitivi Streams di oggetti (serializzazione) File ad accesso diretto
Scansione Scanner Da Java 1.5 il modo più semplice di leggere da una stream di tipo testo: Uno scanner suddivide la stream in tokens utilizzando delimitatori (per default spazi, tabs, newlines …) i tokens possono essere convertiti in valor di tipo primitivo e stringa utilizzando i metodi della classe Scanner in = new Scanner(File(“input.txt“)); Continua…
Scansione Scanner Per effettuare la scansione dell’input utilizziamo i metodi forniti dalla classe Metodi di scansione generica String next(), bool hasNext() cercano il prossimo token (ignorando i delimitatori) Metodi di scansione specifica per tipo int nextInt(), bool hasNextInt() ... Continua…
Scansione Scanner non solo per input da file … Continua…
Scansione
Output Formattato Per formattare un file di testo costruiamo un PrintWriter sul file Se il file esiste al momento della creazione il contenuto viene cancellato Se il file non esiste, la chiamata al costruttore crea un nuovo file, vuoto PrintWriter out = new PrintWriter("output.txt"); Continua…
Output Formattato Utilizziamo print e println per scrivere con PrintWriter: File aperti (in scrittura) devono essere sempre chiusi Altrimenti non abbiamo garanzie che l’output sia effettuato out.println(29.95); out.println(new Rectangle(5, 10, 15, 25)); out.println("Hello, World!"); out.close(); Continua…
Output Formattato PrintWriter non solo per output sul file … Continua…
Output Formattato PrintWriter out = new PrintWriter("output.txt");
Esempio Legge tutte le linee di un file (di tipo testo) e le copia in output precedute dal numero di linea Mary had a little lamb Whose fleece was white as snow. And everywhere that Mary went, The lamb was sure to go! /* 1 */ Mary had a little lamb /* 2 */ Whose fleece was white as snow. /* 3 */ And everywhere that Mary went, /* 4 */ The lamb was sure to go! Continua…
File LineNumberer.java import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.Scanner; public class LineNumberer { public static void main(String[] args) Scanner console = new Scanner(System.in); System.out.print("Input file: "); String inputFileName = console.next(); System.out.print("Output file: "); String outputFileName = console.next(); try Continua…
File LineNumberer.java FileReader reader = new FileReader(inputFileName); Scanner in = new Scanner(reader); PrintWriter out = new PrintWriter(outputFileName); int lineNumber = 1; while (in.hasNextLine()) { String line = in.nextLine(); out.println("/* " + lineNumber + " */ " + line); lineNumber++; } out.close(); } catch (IOException exception) System.out.println("Error processing file:"+ exception);
Domande Che succede se forniamo lo stesso nome per i due file di input e output? Che succede se il nome fornito dall’utente non corrisponde ad alcune file?
Risposte Creando il PrintWriter si azzera il contenuto del file di output. Anche il file di input è quindi nullo, e dunque il ciclo termina immediatamente. Il programma cattura una FileNotFoundException, stampa un messaggio di errore e termina.
Streams Due modalità di memorizzazione e codifica dei dati: formato testo formato binario Due corrispondenti gerarchie di streams nella libreria java.io
Streams di Testo Utilizzate per la lettura scrittura di testo Sono sequenze di caratteri l’intero 12.345 è memorizzato come la seguenza di caratteri '1' '2' '3' '4' '5' Continua…
Streams di Testo Reader, Writer classi astratte che fanno da radici per gerarchia delle stream di testo per le operazioni di input e output Molte sottoclassi, alcune già viste FileReader, Scanner, ... FileWriter, PrintWriter ... Continua…
Streams di Testo Le classi Reader, Writer forniscono i metodi base per l’input/output Reader.read() restituisce il prossimo carattere, come intero -1 se non esiste un nuovo carattere (nd of file) si blocca in attesa di input da tastiera Reader reader = new FileReader(“input.txt”); int next = reader.read(); char c; if (next != -1) c = (char) next; Continua…
Streams di Testo Le classi Reader, Writer forniscono i metodi base per l’input/output Write.write(char c) scrive il carattere sulla stream
Streams Binarie Utilizzate la manipolazione di dati in formato binario Dati rappresentati come sequenze di bytes l’intero 12.345 è memorizzato come la seguenza di quattro bytes 0 0 48 57 Rappresentazione più compatta ed efficiente Continua…
Streams Binarie InputStream, OutputStream classi astratte che fanno da radici per gerarchia delle stream binarie per le operazioni di input e output Anche qui molte sottoclassi concrete FileInputStream, ObjectInputStream, ... FileOutputStream,ObjectOutputStream, ... Continua…
Streams Binarie Le classi InputStream, OutputStream forniscono i metodi base per l’input/output InputStream.read() restituisce il prossimo carattere, come intero -1 se non esiste un nuovo carattere (end of file) OutputStream.write(byte b) scrive b sullo stream Continua…
DataStreams Utilizzate per leggere e scrivere in formato binario di tipi primitivi byte, char, short, ..., double, boolean, Due classi: DataInputStream, DataOutputStream Forniscono metodi per manipolare dati primitivi, readBoolean / writeBoolean readChar / writeChar readInt / writeInt ... / ...
Input/output strutturato Per operazioni più complesse utilizziamo le sottoclassi, come classi decorators Reader reader = new FileReader(“input.txt”)); Scanner input = new Scanner(reader); Writer writer = new FileWriter(“output.txt”)); PrintWriter output = new PrintWriter(writer)); InputStream is = new FileInputStream(“in.bin”)); DataInputStream dis = new DataInputStream(is); OutputStream os = new FileOutputStream(“out.bin”)); DataOutputStream dos = new DataOutputStream(os);
DataOutputStreams Scrive un array di double su un file void writeData(double[] data, String file) throws IOException { DataOutputStream out = new DataOutputStream( new FileOutputStream(file)); out.writeInt(data.length()); for (int i = 0; i < data.length; i++) out.writeDouble(data[i]); out.close(); }
DataInputStreams Legge un file di double, in cui il primo elemento è un intero che rappresenta il numero di valori contenuti nel file restituisce un array con i valori letti double[] readData(String file) throws IOException { DataInputStream in = new DataInputStream( new FileInputStream(file)); double[] data = new double[in.readInt()]; for (int i = 0; i < data.length; i++) data[i] = in.readDouble(); in.close(); return data; }
File Streams Permettono di utilizzare files come streams Due coppie di classi FileInputStream / FileOutputStream FileReader / FileWriter Ognuna di queste classi permette di costruire una stream su un file a partire da: una stringa, il nome del file un oggetto di tipo File un oggetto di tipo FileDescriptor
La classe File Rappresentazione astratta del nome e proprietà di files e directories. Diversi sistemi operativi utilizzano diverse sintassi per i nomi di files: la classe File fornisce una rappresentazione indipendente dal sistema Un nome è rappresentato da una stringa prefissa che rappresenta la radice la radice del file system : ad esempio “/” in unix e “\\” per una sequenza di zero o più nomi, ciascuno dei quali rappresenta un passo sul cammino al file. Tutti i nomi sono directories, ad eccezione dell’ultimo che può essere una directory o un file
ObjectStreams Estensioni delle DataStreams a valori di tipo riferimento Classi: ObjectInputStream e ObjectOutputStream serializzazione/deserializzazione conversione di un oggetto in/da una stream di bytes Metodi ObjectOutputStream.writeObject() ObjectInputStream.readObject()
ObjectStreams writeObject(Object obj): Object readObject() scrive sulla stream una sequenza di bytes corrispondente al grafo che costituisce obj il grafo include tutti i campi primitivi dell’oggetto e tutti gli oggetti raggiungibili da riferimenti presenti in obj; esclusi i campi marcati transient Object readObject() legge dalla stream una sequenza di bytes che rappresenta un grafo e restituisce l’oggetto corrispondente
writeObject() Scrive un intero oggetto sulla stream Anche una lista viene scritta da una sola writeObject BankAccount b = . . .; ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("bank.dat")); out.writeObject(b); ArrayList<BankAccount> a = new ArrayList<BankAccount>(); // ... aggiungi BankAccounts ad a ... out.writeObject(a);
readObject() Restituisce un riferimento di tipo Object Necessario ricordare i tipi degli oggetti scritti sulla stream per leggerli ed utilizzare cast può lanciare ClassNotFoundException eccezione controllata necessario catturare o dichiarare BankAccount b = (BankAccount) in.readObject(); Continued…
Serializable Oggetti scritti su una object stream devono appartenere a classi che implementano l’interfaccia Serializable Serializable è una interfaccia marker non ha metodi, segnala una proprietà. class BankAccount implements Serializable { . . . } Continua…
Serializable Ogni oggetto scritto sulla stream viene associato ad un numero seriale Se lo stesso oggetto viene scritto due volte, la seconda volta si scrive solo il numero seriale, non l’oggetto In fase di lettura, ogni input alloca un nuovo oggetto, a meno che la lettura non incontri un numero seriale duplicato, nel qual caso si restituisce un riferimento allo stesso oggetto
File Serialtester.java public static void main(String[] args) throws IOException, ClassNotFoundException { Bank bank; File f = new File("bank.dat"); if (f.exists()) ObjectInputStream in = new ObjectInputStream (new FileInputStream(f)); bank = (Bank) in.readObject(); in.close(); } else bank = new Bank(); bank.addAccount(new BankAccount(1001, 20000)); bank.addAccount(new BankAccount(1015, 10000)); Continua…
File Serialtester.java // Esegui un deposito BankAccount a = firstBankOfJava.find(1001); a.deposit(100); System.out.println(a.getAccountNumber() + ":" + a.getBalance()); a = firstBankOfJava.find(1015); ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream(f)); out.writeObject(firstBankOfJava); out.close(); }
Serializzazione user-defined In alcune situazioni la serializzazione default può risultare scorretta/inefficiente. hash è transient, perché si può calcolare dal nome class Nome implements Serializable { private String nome; private transient int hash; public Nome (String nome) { this.nome = nome; hash = nome.hashCode();} . . . } Continua…
Serializzazione user-defined Il campo hash è transient, Ma allora deserializzando un Nome perdiamo il suo codice hash … class Nome implements Serializable { private String nome; private transient int hash; public Nome (String nome) { this.nome = nome; hash = nome.hashCode();} . . . } Continua…
Serializzazione user-defined Sia C la nostra classe Serializable Definiamo in C due metodi: void writeObject(ObjectOutputStream) void readObject(ObjectInputStream) Invocati automaticamente da ObjectOutputStream.writeObject(Object o) quando l’argomento ha tipo C ObjectInputStream.readObject()per leggere argomenti scritti da C.writeObject(ObjectOutputStream)
Nome: serializzazione corretta class Nome implements Serializable { private String nome; private transient int hash; public Nome (String nome) { this.nome = nome; hash = name.hasCode();} private void writeObject(ObjectOutputStream out) throws IOException { out.writeUTF(nome); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { nome = in.readUTF(); hash = nome.hashCode();} } Continua…
Nome: serializzazione corretta writeObject / readObject dichiarano IOException invocano metodi che potrebbero lanciarle. readObject dichiara ClassNotFoundException : deserializzazione richiede la costruzione oggetti di classi che devono essere caricate (e quindi trovate) per invocarne i costruttori sono private: per protezione il processo di serializzazione bypassa questo vincolo, per invocare comunque i due metodi, utilizzando i meccanismi di riflessione per disattivare i livelli di accesso
File ad Accesso Casuale Permette di accedere a punti arbitrari di un file Solo per file memorizzati su disco System.in e System.out non supportano queste operazioni Ogni file su disco ha uno stato che determina la posizione corrente di lettura e/o scrittura
Accesso Casuale
RandomAccessFile Possiamo aprire un file in diverse modalità Read-only ("r") Read-write ("rw") Write-only ("w") Il metodo seek() permette di spostare il file-pointer ad una specifica posizione RandomAccessFile f = new RandomAcessFile("bank.dat","rw"); f.seek(n); Continua…
RandomAccessFile f.getFilePointer() restituisce la posizione corrente del file pointer f.length() restituisce la lunghezza del file // tipo "long" perchè un file può avere grandi dimensioni long n = f.getFilePointer(); fileLength = Continua…
RandomAccessFile Memorizza dati in formato binario readInt e writeInt leggono e scrivono interi su 4 bytes readDouble e writeDouble utilizzano 8 bytes
Esempio Utilizziamo un file ad accesso casuale per memorizzare un insieme di conti bancari memorizziamo solo la parte dati di ciascun conto numero di conto e saldo Il programma permette di selezionare un conto ed eseguire operazioni di deposito/prelievo …
Esempio Calcolo del numero di conti sul file public int size() throws IOException { return (int) (file.length() / RECORD_SIZE); // RECORD_SIZE = 12 bytes: // 4 bytes per il numero // 8 bytes for il saldo }
Esempio Accesso (in lettura) all’n-esimo conto sul file public BankAccount read(int n) throws IOException { file.seek(n * RECORD_SIZE); int accountNumber = file.readInt(); double balance = file.readDouble(); return new BankAccount(accountNumber, balance); }
Esempio Accesso (in scrittura) all’n-esimo conto public void write(int n, BankAccount account) throws IOException { file.seek(n * RECORD_SIZE); file.writeInt(account.getAccountNumber()); file.writeDouble(account.getBalance()); }
File BankDatatester.java import java.io.IOException; import java.io.RandomAccessFile; import java.util.Scanner; /** Test per file ad accesso diretto. Un loop di interazione con l’utente in cui si agisce su un conto esistente o se ne crea uno nuovo.. */ public class BankDataTester { public static void main(String[] args) throws IOException Scanner in = new Scanner(System.in); BankData data = new BankData(); try Continua…
File BankDatatester.java { data.open("bank.dat"); boolean done = false; while (!done) System.out.print("Account number: "); int accountNumber = in.nextInt(); System.out.print("Amount to deposit: "); double amount = in.nextDouble(); int position = data.find(accountNumber); BankAccount account; if (position >= 0) account = data.read(position); account.deposit(amount); Continued…
File BankDatatester.java System.out.println("new balance=" + account.getBalance()); } else // Add account { account = new BankAccount(accountNumber, amount); position = data.size(); System.out.println("adding new account"); data.write(position, account); System.out.print("Done? (Y/N) "); String input = in.next(); if (input.equalsIgnoreCase("Y")) done = true; Continua…
File BankDatatester.java finally { data.close(); }
File BankData.java import java.io.IOException; import java.io.RandomAccessFile; /** * Una classe associata ad un file contenente i dati . */ public class BankData { * Costruisce una BankData senza associarla ad un file public BankData() file = null; } Continua…
File BankData.java /** * Apre il file di dati. * @param il nome del file con i dati */ public void open(String filename) throws IOException { if (file != null) file.close(); file = new RandomAccessFile(filename, "rw"); } Continua…
File BankData.java /** * Restituisce il numero di conti sul file */ public int size() throws IOException { return (int) (file.length() / RECORD_SIZE); } * Chiude il file public void close() throws IOException if (file != null) file.close(); file = null; Continua…
File BankData.java /** * Legge un record dal file. */ public BankAccount read(int n) throws IOException { file.seek(n * RECORD_SIZE); int accountNumber = file.readInt(); double balance = file.readDouble(); return new BankAccount(accountNumber, balance); } Continua…
File BankData.java /** * Trova la posizione del conto con il dato numero * @result = la posizione, -1 se il conto non viene trovato */ public int find(int accountNumber) throws IOException { for (int i = 0; i < size(); i++) file.seek(i * RECORD_SIZE); int a = file.readInt(); if (a == accountNumber) // trovato return i; } return -1; // nessun match sull’intero file Continua…
File BankData.java /** Salva un record sul tile @param n l’indice del conto sul file @param il conto da salvare */ public void write(int n, BankAccount account) throws IOException { file.seek(n * RECORD_SIZE); file.writeInt(account.getAccountNumber()); file.writeDouble(account.getBalance()); } private RandomAccessFile file; Continua…
File BankData.java public static final int INT_SIZE = 4; public static final int DOUBLE_SIZE = 8; public static final int RECORD_SIZE = INT_SIZE + DOUBLE_SIZE; }
Output Account number: 1001 Amount to deposit: 100 adding new account Done? (Y/N) N Account number: 1018 Amount to deposit: 200 Amount to deposit: 1000 new balance=1100.0 Done? (Y/N) Y
Esempio: Text Editor Grafico MenuItem
Menu MenuBar MenuItem Menu
Menu Items Aggiungiamo i menu items e i menu in cascata con il metodo add add(): Un menu item genera eventi eventi, più precisamente ActionEvents JMenuItem fileExitItem = new JMenuItem("Exit"); fileMenu.add(fileExitItem); Continua…
Menu Items Aggiungiamo quindi un listener a ciascun item: Il listener viene associato agli items del menu, non al menu o alla barra dei menu fileExitItem.addActionListener(listener);
Struttura dei Menu Items : EditorMenuItem <abstract> PasteMenuItem ClearMenuItem OpenMenuItem FileMenuItem SaveMenuItem ActionListener <interfaccia> FindMenuItem JMenuItem QuitMenuItem
EditorMenuItem /** EditorMenuItem: un menu item generico */ abstract class EditorMenuItem extends JMenuItem implements ActionListener { // il buffer su cui opera il menu item private EditBuffer buffer; public EditorMenuItem(String label, EditBuffer buff) { super(label); buffer = buff; addActionListener(this); } protected EditBuffer getBuffer() { return buffer; } // no actionPerformed(): la classe e` abstract
Clear/Cut/Copy/Paste class ClearMenuItem extends EditorMenuItem { public ClearMenuItem(String label, EditBuffer buff) { super(label, buff); } public void actionPerformed(ActionEvent e) { getBuffer().clear(); } } class CutMenuItem extends EditorMenuItem public CutMenuItem(String label, EditBuffer buff) { getBuffer().cut(); } Continua…
Clear/Cut/Copy/Paste class CopyMenuItem extends EditorMenuItem { public CopyMenuItem(String label, EditBuffer buff) { super(label, buff); } public void actionPerformed(ActionEvent e) { getBuffer().copy(); } } class PasteMenuItem extends EditorMenuItem { public PasteMenuItem(String label, EditBuffer buff) { getBuffer().paste(); }
Find /** * FindMenuItem: genera un dialog per cercare * una stringa nella text area */ class FindMenuItem extends EditorMenuItem { public FindMenuItem(String label, EditBuffer buff) { super(label, buff); } // . . . metodo actionPerformed . . . } Continua…
Find public void actionPerformed(ActionEvent e) { String s = JOptionPane.showInputDialog(this,"Search:"); if ( s != null ) { int index = buffer.findFromCaret(s); if ( index == -1 ) { int response = JOptionPane.showConfirmDialog(this, "Not found. Wrapped Search?"); if ( response == JOptionPane.YES_OPTION ) { index = buffer.findFromStart(s); if ( index == -1 ) JOptionPane.showMessageDialog(thi "Not Found"); }
Quit /** QuitMenuItem: esci dall'editor */ class QuitMenuItem extends JMenuItem implements ActionListener { private EditFrame frame; public QuitMenuItem(String label, EditFrame frame) { super(label); this.frame = frame; addActionListener(this); } public void actionPerformed(ActionEvent e) { if (frame.confirmQuit()) System.exit(0); return;
FileMenuItem abstract class FileMenuItem extends EditorMenuItem { public FileMenuItem(String label, EditBuffer buff) { super(label, buff); } public File chooseFile() { File chosen = null; JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Open"); int result = chooser.showDialog(null,“Open"); if ( result == JFileChooser.APPROVE_OPTION ) { chosen = chooser.getSelectedFile(); return chosen;
JFileChooser
Open class OpenMenuItem extends FileMenuItem { public OpenMenuItem(String label, EditBuffer buff) { super(label, buff); } public void actionPerformed(ActionEvent e) { File f = chooseFile(); if ( f == null) return; getBuffer().setFile(f); try { getBuffer().setText(""); getBuffer().openFile(); } catch (IOException exc) { JOptionPane.showMessageDialog(null, "Could not open File");
EditBuffer public class EditBuffer extends JTextArea { private File f; // il file associato al buffer public EditBuffer(File initFile, int rows, int cols) { super("", rows, cols); f = initFile; setLineWrap(true); setFont(new Font("Courier", Font.PLAIN, 14)); if (f == null) return; try { openFile(); } catch (IOException e) { JOptionPane.showMessageDialog(this,"File not Found!"); } . . . Continua…
EditBuffer public void openFile() throws IOException { Scanner opened = null; try { FileReader r = new FileReader(f); opened = new Scanner(r); while ( opened.hasNextLine() ) { String s = opened.nextLine(); append(s+"\n"); } } finally { if (opened != null) opened.close(); Continua…
EditBuffer public void saveBuffer() throws IOException, NoFileSelectedException { PrintWriter p = null; if (f == null) throw new NoFileSelectedException(); try { FileWriter w = new FileWriter(f); p = new PrintWriter(w); p.print(getText()); } finally { if (p != null) p.close(); }