In questo articolo vedremo come si gestiscono input ed output nei nostri programmi in Java.
In generale, da Java, come mezzi di input utilizzeremo:
- il disco fisso, ovvero input da file;
- La tastiera, ovvero input da utente.
Per quanto riguarda l’ output invece, può essere:
- Il disco fisso, ovvero output su file;
- Lo schermo, ovvero la stampa a video.
Cominciamo a parlare dell ‘output e più precisamente con:
Output su schermo
L’output su schermo è praticamente quando facciamo apparire nella consolle un messaggio. Per farlo, abbiamo già usato (senza analizzarlo nel dettaglio) System.out, oggetto dello standard output.
Out è un campo public, static, final di System di tipo java.io.PrintStream (che estende java.io.OuputStream).
PrintStream possiede diversi metodi utili per stampare a video: i più importanti sono
print, che printa una scritta:
System.out.print("Ciao, Mondo!");
println, che stampa una scritta e poi un “accapo” (ovvero se dopo facciamo un’altro print, questo verrà stampato a capo):
System.out.println("Ciao,Mondo!");
format, che riprende lo stile del C:
String s = "ciao"; System.out.format("La prima lettera è: %c", s.charAt(0));
Input da Tastiera
Per prendere input da tastiera invece, usiamo la classe java.util.Scanner che è costruita con lo standard input Stream:
System.in è un campo static, public, final di System di tipo java.io.InputStream.
Vediamo come utilizzarla in pratica:
Scanner s = new Scanner(System.in); //Creo lo scanner int k = s.nextInt(); //Chiedo un numero System.out.println("Hai digitato:"+k); String string = s.next(); //Chiedo una stringa System.out.println("Hai digitato la stringa:"+string);
I metodi di java.io.InputStream non vengono utilizzati direttamente, perciò non sono molto importanti da conoscere.
La classe java.util.Scanner invece ha alcuni meodi utili che conviene conoscere:
nextLine(): ci ritorna la prossima linea, ovvero se noi inseriamo come input:
Ciao, come stai?
E usiamo next(), ci viene ritornata una parola per volta. Con nextLine(), tutta la linea.
Inoltre, quando creiamo l’oggetto scanner, possiamo utilizzare il metodo useDelimiters(Pattern) che ci permette di specificare un delimitatore.
Input da File
Per quanto riguarda la lettura dell’ input da file, abbiamo due modi per farlo.
Prima di vederli, ricordiamo che un file di testo è formato da linee (le righe) e che il carattere “a capo” che viene utilizzato come fine linea, è il carattere backslash n:
\n
Mentre esistono anche file di tipo binario, che hanno un senso solo se aperti con il programma che li ha creati o un programma creato apposta per leggerli (per esempio: prova ad aprire un’immagine con un editor di testo).
Lo stream
Uno stream è un’astrazione derivata dai dispositivi di input/output sequenziali.
Uno stream di input quindi produce uno stream di caratteri, mentre uno stream di output riceve uno stream di caratteri “uno alla volta”.
Un file può essere trattato sia come uno stream di input o output anche se in realtà i file sono bufferizzati per questioni di efficenza
Le gerarchie di classi per lo stream in java, abbiamo quattro gerarchie.
- Per leggere caratteri (file/buffer)Reader/(file/buffer)Writer (file di testo)
- Per leggere file binari InputStream/OutputStream
Il primo tipo, non è deprecato ma volendo abbiamo sistemi più potenti ma meno efficenti come java.util.Scanner
Per poter fare in modo che il nostro programma sia WORA (write once run anywhere), dobbiamo ricordarci una cosa: in Windows, la path di una cartella può essere così:
C:\Users\Utente\Documenti
In linux invece, una directory è:
/home/utente/Desktop
Questo per dire di non inserire mai e poi mai percorsi assoluti: questo perchè non sappiamo in che cartella il nostro programma verrà installato/avviato, e perchè non sappiamo su quale sistema.
Se volete quindi includere dei file, utilizzate il campo File.separator, che permette di avere il separatore di directory utilizzato dal sistema.
Vediamo ora come leggere un file utilizzando il moderno (ma ripetiamo meno efficente) Scanner:
File f = new File ("mio_file.txt"); //Creo un oggetto File try { Scanner in = new Scanner(f); while(in.hasNext()) { System.out.println(in.next()); } } catch(FileNotFoundException e) { e.printStackTrace() }
Per leggere un file in maniera efficente:
BufferedReader br = null; try { br = new BufferedReader( new FileReader("file.txt")); while(br.ready()) { System.out.println(br.readLine()); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(br != null) br.close(); } catch(IOException e) { e.printStackTrace(); } }
In entrambi i casi, dobbiamo inserire l’apertura dei file in un blocco try-catch perchè entrambe possono rilasciare un’eccezione nel caso non trovino il file .
Nel secondo caso, utilizziamo la parola chiave finally per chiudere il buffer e quindi liberare la memoria allocata.
Entrambi i modi ci permettono di leggere da file, per file molto grandi è consigliato il secondo modo (che oltre che più efficiente, è anche specializzato per questo scopo al contrario di Scanner che abbiamo già visto prima).
Output su File
Anche in questo caso, abbiamo due modi. La sintassi è simile agli ultimi due esempi, rispettiamo l’ordine e vediamo quello più facile da ricordare:
File f = new File("file.txt"); try { PrintWriter outPut = new PrintWriter(f); out.println("Prima Riga da scrivere"); out.close(); } catch(FileNotFoundException e) { e.printStackTrace(); }
Come potete notare, la sintassi di PrintWriter è simile a quella della lettura tramite Scanner: abbiamo un blocco try-catch dove apriamo il file, scriviamo una linea (seguita da accapo), e chiudiamo il file. L’eccezione emessa è di tipo file non trovato.
Usando invece FileWriter–BufferedWriter:
BufferedWriter bw = null; try { bw = new BufferedWriter(new FileWriter("file.txt")); bw.write("Questo testo verrà scritto nel file."); } catch(IOException e) { e.printStackTrace(); } finally { try { if(bw != null) bw.close(); } catch(IOException e) { e.printStackTrace(); } }