Domanda Chiarimenti sull'input

Stato
Discussione chiusa ad ulteriori risposte.

Laempo

Utente Electrum
1 Agosto 2014
287
16
100
147
Salve,

desideravo avere dei chiarimenti circa le varie possibilità di input disponibili in Java. Tuttavia fra quelle disponibili ( e a me note - se ve ne sono altre di vantaggiose vi invito caldamente a farmelo presente) vorrei approfondire:
BufferedReader, InputStreamReader, Scanner.
Solitamente uso, per formazione scolastica, il BufferedReader in questo modo:
Java:
// simulo un metodo per un inserimento di dati
public void inserimentoDati()
{
BufferedReader tastiera = new BufferedReader (new InputStreamReader(System.in));
try {
       int a = Integer.parseInt(tastiera.readLine());
       String a = tastiera.getLine();
     }
catch (Exception e)
{
   System.out.println("Errore.");
}
}
Scanner invece:
Java:
public void inserimentoDati() 
{
 Scanner sc= new Scanner(System.in);
 int i= scan.nextInt();
 String varString = scan.nextLine();
}

I miei dubbi sono i seguenti:
1. Un oggetto BufferedReader ha un buffer maggiore rispetto ad uno di tipo Scanner, di conseguenza per immagazzinare stringhe di notevoli dimensioni è poco conveniente quest'ultimo. Tuttavia Scanner mi permette di prelevare solo ciò che io desidero da un buffer (eg. nextInt() ).
Posso fare qualcosa del genere con un oggetto BufferedReader?
2. L'uso del BufferedReader richiede (necessariamente da quanto ho capito) il costrutto try-catch per gestire le eccezioni. È decisamente comoda come cosa, ma non posso applicare la stessa cosa a Scanner ed appianare questa differenza?
3. Date uno sguardo al seguente codice. Quali sono le differenze fra questo codice e il primo da me postato ( l'esempio del BufferedReader):
Java:
String rigaLetta = "";
InputStreamReader tastiera = new InputStreamReader(System.in);
BufferedReader bufferTastiera = new BufferedReader(tastiera);

try 
{
 rigaLetta = bufferTastiera.readLine();
}
catch (IOException e)
{
e.printStackTrace();
}

Ormai dovrebbe essere chiaro. Il mio obiettivo è capire quale dei due metodi di inserimento dati è preferibile utilizzare a seconda delle situazioni e se in alcuni non vi sono differenze sostanziali.

Grazie anticipatamente
 
La dimensione del buffer per me non è un fattore rilevante, sono entrambi sufficientemente grandi per le tipiche letture da tastiera. Lo scanner, come hai giustamente notato, è qualcosa di molto più sofisticato: non è una classe pensata per leggere e basta, è pensata per fare un analisi lessicale e prelevare cose come gli Integer.

L'uso del BufferedReader richiede (necessariamente da quanto ho capito) il costrutto try-catch per gestire le eccezioni. È decisamente comoda come cosa, ma non posso applicare la stessa cosa a Scanner ed appianare questa differenza?
Comoda? Per me è molto scomoda. Alla fine per ottenere un IOException da System.in deve succedere proprio qualcosa di molto particolare. Per come la vedo io, se capita, tanto vale far crashare il programma.
Stabilito che ottenere un IOException da System.in è molto improbabile, perché dovresti volere un try-catch? La classe Scanner ha già dei metodi (eg. hasNextInt) che ti permettono di fare i dovuti controlli.

Un oggetto BufferedReader ha un buffer maggiore rispetto ad uno di tipo Scanner, di conseguenza per immagazzinare stringhe di notevoli dimensioni è poco conveniente quest'ultimo. Tuttavia Scanner mi permette di prelevare solo ciò che io desidero da un buffer (eg. nextInt() ).
Posso fare qualcosa del genere con un oggetto BufferedReader?
La classe Scanner è fatta apposta per questo. Vuoi scannerizzare un BufferedReader? Passi il BufferedReader a Scanner.

Per come la vedo io, la cosa migliore da fare è questa:
Java:
String input = System.console().readLine();
Una linea di codice, facile e pulito.
 
  • Mi piace
Reazioni: Laempo
Ultima modifica:
Sono d'accordo con te, ma qui:
Per come la vedo io, la cosa migliore da fare è questa:

Una linea di codice, facile e pulito.
non proprio. La documentazione ci dice che se la vm ha una console è dipendente sia dal modo in viene invocata, che dalla piattaforma sottostante. Se non c'è nessuna console ti viene returnato null.

Console c = System.console();
if (c != null) {
String s = c.readLine();
} else {
// niente console
}

Meglio utilizzare la classe Scanner.
 
Ultima modifica:
Comoda? Per me è molto scomoda. Alla fine per ottenere un IOException da System.in deve succedere proprio qualcosa di molto particolare. Per come la vedo io, se capita, tanto vale far crashare il programma.
Stabilito che ottenere un IOException da System.in è molto improbabile, perché dovresti volere un try-catch? La classe Scanner ha già dei metodi (eg. hasNextInt) che ti permettono di fare i dovuti controlli.
Vedere il costrutto try-catch scomodo è un nuovo punto di vista; non mi sarei mai immaginato di poterlo ritenere uno svantaggio.
Tuttavia riflettendoci un attimo, ragionando nell'ottica di idee di dover realizzare un programma particolarmente rigido/sicuro, la possibilità di effettuare controlli sulla validità dell'input è sufficientemente coperta da altri metodi (sto pensando a if e for).

Per quanto riguarda la possibilità di gestire i controlli con metodi tipo hasNextInt mi è nuova: non ho ancora approfondito molto la classe Scanner. A questo punto direi che è più che soddisfacente come cosa.
Sono d'accordo con te, ma qui:

non proprio. La documentazione ci dice che se la vm ha una console è dipendente sia dal modo in viene invocata, che dalla piattaforma sottostante. Se non c'è nessuna console ti viene returnato null.

Console c = System.console();
if (c != null) {
String s = c.readLine();
} else {
// niente console
}

Meglio utilizzare la classe Scanner.
A questo punto dire convinto. Anche se mi ha incuriosito l'utilizzo della console.
È utilizzabile in modo sicuro?
 
È utilizzabile in modo sicuro?
Sì. Utilizzare la classe Scanner è il metodo più facile e veloce per ottenere l'input dell'utente.
È una maggiore astrazione, molto più robusta e nuova confrontata al vecchio BufferedReader. Certo, BufferedReader forse è più veloce dello Scanner, ma il fatto che Scanner abbia più caratteristiche credo spieghi tutto. Non mi preoccuperei comunque molto in questi casi.
 
  • Mi piace
Reazioni: Laempo
Grazie ugualmente dell'informazione, però mi riferivo a console.
Non nego che la domanda era fuorviante.
 
non proprio. La documentazione ci dice che se la vm ha una console è dipendente sia dal modo in viene invocata, che dalla piattaforma sottostante. Se non c'è nessuna console ti viene returnato null.
E cosa fai se non hai la console? Per me è uno di quei casi dove, sti cazzi... lasciamo crashare il programma.

Fatico ad immaginarmi un error recovery che abbia un minimo di senso: ho bisogno di un input e l'utente non ha una console su cui può scrivere... addio. Se proprio qualcuno sente il bisogno di controllare anche questo caso, gli consiglierei di mettere l'if o il try catch allo stesso modo in cui hai fatto tu, ma è un caso talmente raro che per me non giustifica la perdita in leggibilità e semplicità (una riga di codice, nessun oggetto creato).

Se non ho la console lo Scanner come si comporta? Suppongo ci voglia il try catch anche li, altrimenti crasha pure quello.

BufferedReader lancia un'eccezione perché potresti utilizzarlo per files e molte altre cose, Scanner è pensato principalmente per estrarre una particolare informazione o token (eg. un Integer). System.console() e Console.readLine() invece sono pensati appositamente per ritornare la console e leggere una linea da questa.


Vedere il costrutto try-catch scomodo è un nuovo punto di vista; non mi sarei mai immaginato di poterlo ritenere uno svantaggio.
A mio parere è uno dei problemi principali di Java. È pensato per essere un linguaggio safe, ma la soluzione che hanno trovato è fin troppo restrittiva e come conseguenza troppa gente le usa male. Troppi tipi di eccezione, poi per crearne una nuova devi definire una nuova classe, se è pubblica gli devi pure fare il file apposta.
Io proprio non riesco a digerirle.

Tuttavia riflettendoci un attimo, ragionando nell'ottica di idee di dover realizzare un programma particolarmente rigido/sicuro, la possibilità di effettuare controlli sulla validità dell'input è sufficientemente coperta da altri metodo (sto pensando a if e for).
Ecco, no.
Se vuoi garantire safety ha perfettamente senso tenere separati i tipi di ritorno dalla gestione degli errori e rendere esplicita un po' di roba. Il fatto che in Java, secondo me, questo sistema è stato progettato in modo pessimo, non implica che farlo a mo dì C sia l'ideale.

È utilizzabile in modo sicuro?
Puoi mettere l'if. Con un solo if copri tutte le letture che farai in futuro:
Java:
Console console = System.console();
if (console == null) {
    // logging dell'errore
    System.exit(1);
}

// da qui in avanti puoi usare console.readLine() in modo safe
 
  • Mi piace
Reazioni: Laempo
Vedrò di dare un'occhiata sia a Scanner che a Console dunque. Sarà sicuramente un passo in avanti rispetto a BufferedReader.
Ringrazio molto entrambi.
 
E cosa fai se non hai la console? Per me è uno di quei casi dove, sti cazzi... lasciamo crashare il programma.
Quella che potrebbe non avere una console iterattiva è l'IDE.
Sicuramente eclipse e netbeans avevano e hanno risolto, o improbabilmente hanno ancora questo problema.
Fatto sta che se rilasci un codice sorgente con System.console().readLine(); all'80% ne varrebbe la pena mettere l'if, almeno chi prova ad avviarlo da IDE capirà cosa sta succedendo. È sicuro che ancora molti IDE hanno questo bug, dato che non è solito che abbiano una console.
Sicuramente non conviene cambiare il codice solo perchè l'ide ha questo problema, ma ti darà fastidio e ti scazza soprattutto se volessi debuggare il tuo codice, e quindi dovrai cambiare codice.

gli consiglierei di mettere l'if o il try catch allo stesso modo
L'if basta ed è molto più efficiente e previeni veramente l'eccezione, non c'è bisogno del try catch.
 
Stato
Discussione chiusa ad ulteriori risposte.