1. InputStreamReaderclasse

Un'altra caratteristica interessante dei flussi è la possibilità di combinare più flussi insieme in catene . Un flusso può leggere i dati non solo dalla sua origine dati interna, ma anche da un altro flusso .

Si tratta di un meccanismo molto potente in Java, che consente di creare complessi scenari di lettura dei dati collegando uno stream all'altro. Tale schema è simile a questo:

Classe InputStreamReader

Quando un programma legge i dati da un flusso di dati, il flusso di dati a sua volta legge i dati dalla sua origine dati, che è ad esempio un altro flusso di dati o un file.

Inoltre, ogni flusso di dati non solo legge e fornisce dati, ma può anche trasformarli o eseguire varie operazioni su di essi. Un buon esempio di tale "flusso intermedio" è la InputStreamReaderclasse.

Conosciamo già una classe chiamata FileReader— è una Readerche legge i dati da un file. E da dove InputStreamReaderprende i suoi dati? Esatto, da un file InputStream.

Quando crei un InputStreamReaderoggetto, devi passare un InputStreamoggetto o una delle sue classi discendenti. Esempio:

String src = "c:\\projects\\log.txt";
FileInputStream input = new FileInputStream(src);
InputStreamReader reader = new InputStreamReader(input);

La InputStreamReaderclasse ha tutti i metodi che Readerha la classe e funzionano esattamente allo stesso modo.

La principale differenza tra la InputStreamReaderclasse e, diciamo, FileReaderè da dove leggono i dati. FileReaderlegge i dati da un file (duh, ecco perché si chiama FileReader), ma InputStreamReaderlegge i dati da un file InputStream.

Quando leggi un carattere da un FileReaderoggetto usando il read()metodo, a sua volta legge due byte dal file su disco e li restituisce come chars.

Quando leggi un carattere da un InputStreamReaderoggetto usando il read()metodo, a sua volta legge due byte dall'oggetto che FileInputStreamgli è passato, che a sua volta legge i dati dal file. Il risultato è una catena di chiamate ai read()metodi.


2. BufferedReaderclasse

Un'altra classe interessante che probabilmente userai molto è BufferedReader. Questo è anche un "flusso intermedio" che legge i dati da un altro flusso.

Come suggerisce il nome, la BufferedReaderclasse è una sottoclasse di Readere ti consente di leggere i caratteri . Ma la cosa più interessante è che devi anche passargli un'origine dati sotto forma di un flusso da cui possono essere letti i caratteri , cioè un flusso che eredita la Readerclasse.

Qual e il punto? A differenza di InputStreamReader, la BufferedReaderclasse non converte i byte in caratteri: non converte nulla. Invece, bufferizza i dati .

Quando un programma legge un singolo carattere da un BufferedReaderoggetto, l'oggetto legge una vasta gamma di caratteri dal suo flusso di origine tutto in una volta. E li memorizza internamente.

Quando il carattere successivo viene letto dall'oggetto BufferedReader, prende semplicemente il carattere successivo dal suo array di buffer interno e lo restituisce senza accedere all'origine dati. Solo quando tutti i caratteri nel buffer sono esauriti, legge un'altra grande matrice di caratteri.

La BufferedReaderclasse ha anche un metodo molto utile — String readLine(), che ti consente di leggere intere stringhe di dati dal flusso di origine tutto in una volta. Puoi usare questo metodo, ad esempio, per leggere un file e visualizzarne il contenuto sullo schermo riga per riga. Esempio:

Abbiamo specificamente scritto un codice compatto per illustrare quanto questo possa essere conveniente. Questo codice potrebbe anche essere scritto con qualche dettaglio in più.

String src = "c:\\projects\\log.txt";

try(FileReader in = new FileReader(src);
BufferedReader reader = new BufferedReader(in))
{
   while (reader.ready())
   {
      String line = reader.readLine();
      System.out.println(line);
   }
}
Crea un FileReaderoggetto. L'origine dati è un file.
Crea un BufferedReaderoggetto. L'origine dati è un file FileReader.
Finché ci sono ancora dati nel lettore
Leggi una riga
Visualizza la riga
Un punto importante:

Se si concatenano più flussi, il close()metodo deve essere chiamato solo su uno di essi. Quel flusso chiamerà il metodo sulla sua origine dati e così via, fino a quando non close()viene chiamato sul flusso di dati finale.



3. Lettura dalla console

E un altro fatto interessante: la Scannerclasse non è altro che un flusso di input intermedio che legge i dati da System.in, che è anche un flusso di dati.

Ecco due modi per leggere una riga dalla console:

Classe Scanner Classi BufferedReader e BufferedWriter
InputStream stream = System.in;
Scanner console = new Scanner(stream);
String line = console.nextLine();
InputStream stream = System.in;
InputStreamReader reader = new InputStreamReader(stream);
BufferedReader buff = new BufferedReader(reader);
String line = buff.readLine();

Il nostro amico non è altro che una variabile statica della classe. È un il cui nome è .System.ininSystemInputStreamin

Quindi quasi dall'inizio dei tuoi studi Java su CodeGym, hai lavorato con flussi di dati e ne hai costruito catene. Ma ora lo farai in modo più consapevole.