Lo scanner salta nextLine () dopo aver usato next () o nextFoo ()?


684

Sto usando i Scannermetodi nextInt()e nextLine()per leggere l'input.

Sembra così:

System.out.println("Enter numerical value");    
int option;
option = input.nextInt(); // Read numerical value from input
System.out.println("Enter 1st string"); 
String string1 = input.nextLine(); // Read 1st string (this is skipped)
System.out.println("Enter 2nd string");
String string2 = input.nextLine(); // Read 2nd string (this appears right after reading numerical value)

Il problema è che dopo aver inserito il valore numerico, il primo input.nextLine()viene ignorato e il secondo input.nextLine()viene eseguito, in modo che il mio output sia simile al seguente:

Enter numerical value
3   // This is my input
Enter 1st string    // The program is supposed to stop here and wait for my input, but is skipped
Enter 2nd string    // ...and this line is executed and waits for my input

Ho testato la mia applicazione e sembra che il problema risieda nell'uso input.nextInt(). Se lo elimino, entrambi string1 = input.nextLine()e string2 = input.nextLine()vengono eseguiti come voglio che siano.


5
Oppure potresti essere come me e usare BufferedReader :) Non mi interessa se è vecchia scuola, ha sempre funzionato e funzionerà sempre per me. Inoltre, la conoscenza di BufferedReader ha applicazione altrove. Semplicemente non mi piace Scanner.
Cruncher,

Risposte:


831

Questo perché il Scanner.nextIntmetodo non legge il carattere di nuova riga nell'input creato premendo "Invio" e quindi la chiamata a Scanner.nextLinerestituisce dopo aver letto quella nuova riga .

Incontrerai un comportamento simile quando usi il metodo Scanner.nextLineafter Scanner.next()o any Scanner.nextFoo(tranne se nextLinestesso).

Soluzione:

  • O fai una Scanner.nextLinechiamata dopo ciascuna Scanner.nextInto Scanner.nextFooper consumare il resto di quella linea compresa la nuova linea

    int option = input.nextInt();
    input.nextLine();  // Consume newline left-over
    String str1 = input.nextLine();
    
  • O, ancora meglio, leggi l'input Scanner.nextLinee converti l'input nel formato corretto di cui hai bisogno. Ad esempio, è possibile convertire in un numero intero utilizzando il Integer.parseInt(String)metodo.

    int option = 0;
    try {
        option = Integer.parseInt(input.nextLine());
    } catch (NumberFormatException e) {
        e.printStackTrace();
    }
    String str1 = input.nextLine();
    

4
@blekione. Devi usare try-catch, perché Integer.parseIntgenera NumberFormatExceptionquando gli viene passato un argomento non valido. In seguito imparerai l'eccezione. Per EG: - Integer.parseInt("abc"). Non vuoi che "abc" venga convertito in int giusto?
Rohit Jain,

3
@blekione. Quindi, nel caso sopra, il tuo codice si fermerà a quel punto e non sarai in grado di continuare l'esecuzione. Con la gestione delle eccezioni, è possibile gestire questo tipo di condizioni.
Rohit Jain,

4
Fino a che punto è meglio quest'ultimo? Lo scanner AFAIK # nextInt () è molto più indulgente nella ricerca di interi corretti, consentendo virgole di gruppo, prefissi e suffissi locali. Numero intero # parseInt () consente solo cifre e punto decimale più un segno opzionale.
Mordechai,

5
Personalmente preferisco un Scanner#hasNextFoocontrollo in anticipo piuttosto che un try-catch, ma anche quello funziona.
Leggermente mediocre il

1
Utilizzare ParseDouble presenta un nuovo problema, ovvero nextDouble utilizza la configurazione regionale del decimale (. O,) ma Parsedouble riceve sempre il decimale degli Stati Uniti (.).
olmerg,

209

Il problema è con il metodo input.nextInt () : legge solo il valore int. Quindi, quando continui a leggere con input.nextLine () ricevi il tasto Invio "\ n". Quindi per saltare questo devi aggiungere input.nextLine () . Spero che questo dovrebbe essere chiaro ora.

Provalo così:

System.out.print("Insert a number: ");
int number = input.nextInt();
input.nextLine(); // This line you have to add (It consumes the \n character)
System.out.print("Text1: ");
String text1 = input.nextLine();
System.out.print("Text2: ");
String text2 = input.nextLine();

umm sembra una soluzione, rendendo la linea saltata nextLine
inutilizzata


Domanda: posta il 27 ottobre 12 alle 16:37. rispose il 14 agosto 11 alle 12:24. Come è possibile?
Apoorv Patne,

79

È perché quando si inserisce un numero, quindi premere Enter, input.nextInt()consuma solo il numero, non la "fine riga". Quando input.nextLine()viene eseguito, consuma la "fine riga" ancora nel buffer dal primo input.

Invece, usare input.nextLine()subito dopoinput.nextInt()


7
@Victor non è un bug. funziona come specificato. potresti argomentare che dovrebbe esserci un modo semplice per dire all'API di consumare anche qualsiasi spazio bianco dopo l'input target.
Boemia

Vedo. grazie @Bohemian, questo è esattamente ciò di cui sto discutendo. Penso che questo dovrebbe essere cambiato, forse puoi indicarmi dove suggerire questo "problema" nel prossimo JCP.
Victor,

@victor Puoi richiedere (e scoprire come contribuire al codice) una funzione tramite la pagina Segnala un bug o Richiedi una funzione .
Boemia


48

Sembra che ci siano molte domande su questo problema con java.util.Scanner. Penso che una soluzione più leggibile / idiomatica sarebbe quella di chiamare scanner.skip("[\r\n]+")per eliminare tutti i caratteri di nuova riga dopo aver chiamato nextInt().

EDIT: come indicato di seguito da @PatrickParker, questo causerà un ciclo infinito se l'utente immette uno spazio bianco dopo il numero. Vedi la loro risposta per un modello migliore da usare con skip: https://stackoverflow.com/a/42471816/143585



So cosa facciamo per rimuovere i dati nel buffer, ma in questo caso, per favore aiutatemi con questo: stackoverflow.com/questions/33585314/having-issues-with-scanner
Khuong

1
Cordiali saluti, questo causerà un ciclo infinito se l'utente digita qualsiasi scheda o spazio dopo l'input numerico. Vedi la mia risposta qui per un salto migliore: stackoverflow.com/a/42471816/7098259
Patrick Parker,

@PatrickParker: in effetti provoca un ciclo infinito! Grazie, ho modificato la risposta per collegarti alla tua.
Denis Tulskiy,

33

Lo fa perché input.nextInt();non cattura la nuova riga. potresti fare come gli altri proposti aggiungendo un input.nextLine();sotto.
In alternativa puoi farlo in stile C # e analizzare un nextLine in un numero intero in questo modo:

int number = Integer.parseInt(input.nextLine()); 

In questo modo funziona altrettanto bene e ti fa risparmiare una riga di codice.



1
devi usare provare a catturare qui. Cosa succederà se l'ingresso non è un numero. NumberFormatException deve essere gestito qui.
Arundev,

Questo presuppone che ci sia un solo token int su ogni riga per cui lo provi.
cellepo,

25

TL; DR Utilizzare scanner.skip("\\R")prima di ogni scanner.newLine()chiamata, che viene eseguita dopo:

  • scanner.next()
  • scanner.next*TYPE*() metodo.

Cose che devi sapere:

  • il testo che rappresenta poche righe contiene anche caratteri non stampabili tra le righe (li chiamiamo separatori di riga) come

    • ritorno a capo (CR - in stringhe letterali rappresentate come "\r")
    • avanzamento riga (LF - in letterali String rappresentati come "\n")
  • quando stai leggendo i dati dalla console, consente all'utente di digitare la sua risposta e quando ha finito deve in qualche modo confermare tale fatto. Per fare ciò, l'utente è tenuto a premere il tasto "invio" / "ritorno" sulla tastiera.

    Ciò che è importante è che questa chiave oltre a garantire il posizionamento dei dati utente nell'input standard (rappresentato dal System.inquale viene letto da Scanner) invia anche separatori di riga dipendenti dal sistema operativo (come per Windows \r\n) dopo di essa.

    Pertanto, quando si chiede all'utente un valore simile age, e l'utente digita 42 e preme invio, l'input standard conterrà "42\r\n".

Problema

Scanner#nextInt(e di altri metodi) non permette scanner a consumare questi separatori di linea. Li leggerà da (in che altro modo Scanner potrebbe sapere che non ci sono più cifre dell'utente che rappresentano un valore rispetto agli spazi bianchi?) Che li rimuoverà dall'input standard, ma memorizzerà anche nella cache quei separatori di riga internamente . Quello che dobbiamo ricordare è che tutti i metodi dello scanner eseguono sempre la scansione a partire dal testo memorizzato nella cache. Scanner#nextTypeSystem.inage

Ora Scanner#nextLine()raccoglie e restituisce semplicemente tutti i caratteri fino a quando non trova i separatori di riga (o la fine del flusso). Ma poiché i separatori di riga dopo aver letto il numero dalla console si trovano immediatamente nella cache di Scanner, restituisce String vuoto, il che significa che Scanner non è stato in grado di trovare alcun carattere prima di quei separatori di riga (o fine dello stream).
BTW consumanextLine anche quei separatori di linea.

Soluzione

Quindi, quando vuoi chiedere il numero e poi l'intera riga, evitando quella stringa vuota come risultato di nextLineentrambi

  • consuma il separatore di riga lasciato dalla nextIntcache degli scanner da
    • chiamando nextLine,
    • o il modo più leggibile di IMO sarebbe chiamando skip("\\R")o skip("\r\n|\r|\n")lasciando che Scanner salti la parte abbinata dal separatore di linea (maggiori informazioni su \R: https://stackoverflow.com/a/31060125 )
  • non usare nextInt(né next, né alcun nextTYPEmetodo) affatto. Invece leggi interi dati riga per riga usando nextLinee analizza i numeri da ciascuna riga (supponendo che una riga contenga solo un numero) per digitare correttamente come intvia Integer.parseInt.

A proposito : i metodi possono saltare i delimitatori (per impostazione predefinita tutti gli spazi bianchi come schede, separatori di riga) inclusi quelli memorizzati nella cache dallo scanner, fino a quando non troveranno il successivo valore non delimitatore (token). Grazie a ciò per input come codiceScanner#nextType"42\r\n\r\n321\r\n\r\n\r\nfoobar"

int num1 = sc.nextInt();
int num2 = sc.nextInt();
String name = sc.next();

sarà in grado di assegnare correttamente num1=42 num2=321 name=foobar.


14

Invece di input.nextLine()usare input.next(), questo dovrebbe risolvere il problema.

Codice modificato:

public static Scanner input = new Scanner(System.in);

public static void main(String[] args)
{
    System.out.print("Insert a number: ");
    int number = input.nextInt();
    System.out.print("Text1: ");
    String text1 = input.next();
    System.out.print("Text2: ");
    String text2 = input.next();
}


13

Se vuoi leggere sia stringhe che ints, una soluzione è usare due scanner:

Scanner stringScanner = new Scanner(System.in);
Scanner intScanner = new Scanner(System.in);

intScanner.nextInt();
String s = stringScanner.nextLine(); // unaffected by previous nextInt()
System.out.println(s);

intScanner.close();
stringScanner.close();

1
Questo è quello che stavo cercando, da un po 'di tempo ormai. Mossa intelligente!
Liam Wilson,

1
@BdLearner scanner.nextLine()dovrebbe funzionare.
André Valenti,

11

Se si desidera eseguire la scansione rapida dell'input senza confondersi con il metodo nextLine () della classe Scanner, utilizzare lo scanner di input personalizzato.

Codice :

class ScanReader {
/**
* @author Nikunj Khokhar
*/
    private byte[] buf = new byte[4 * 1024];
    private int index;
    private BufferedInputStream in;
    private int total;

    public ScanReader(InputStream inputStream) {
        in = new BufferedInputStream(inputStream);
    }

    private int scan() throws IOException {
        if (index >= total) {
            index = 0;
            total = in.read(buf);
            if (total <= 0) return -1;
        }
        return buf[index++];
    }
    public char scanChar(){
        int c=scan();
        while (isWhiteSpace(c))c=scan();
        return (char)c;
    }


    public int scanInt() throws IOException {
        int integer = 0;
        int n = scan();
        while (isWhiteSpace(n)) n = scan();
        int neg = 1;
        if (n == '-') {
            neg = -1;
            n = scan();
        }
        while (!isWhiteSpace(n)) {
            if (n >= '0' && n <= '9') {
                integer *= 10;
                integer += n - '0';
                n = scan();
            }
        }
        return neg * integer;
    }

    public String scanString() throws IOException {
        int c = scan();
        while (isWhiteSpace(c)) c = scan();
        StringBuilder res = new StringBuilder();
        do {
            res.appendCodePoint(c);
            c = scan();
        } while (!isWhiteSpace(c));
        return res.toString();
    }

    private boolean isWhiteSpace(int n) {
        if (n == ' ' || n == '\n' || n == '\r' || n == '\t' || n == -1) return true;
        else return false;
    }

    public long scanLong() throws IOException {
        long integer = 0;
        int n = scan();
        while (isWhiteSpace(n)) n = scan();
        int neg = 1;
        if (n == '-') {
            neg = -1;
            n = scan();
        }
        while (!isWhiteSpace(n)) {
            if (n >= '0' && n <= '9') {
                integer *= 10;
                integer += n - '0';
                n = scan();
            }
        }
        return neg * integer;
    }

    public void scanLong(long[] A) throws IOException {
        for (int i = 0; i < A.length; i++) A[i] = scanLong();
    }

    public void scanInt(int[] A) throws IOException {
        for (int i = 0; i < A.length; i++) A[i] = scanInt();
    }

    public double scanDouble() throws IOException {
        int c = scan();
        while (isWhiteSpace(c)) c = scan();
        int sgn = 1;
        if (c == '-') {
            sgn = -1;
            c = scan();
        }
        double res = 0;
        while (!isWhiteSpace(c) && c != '.') {
            if (c == 'e' || c == 'E') {
                return res * Math.pow(10, scanInt());
            }
            res *= 10;
            res += c - '0';
            c = scan();
        }
        if (c == '.') {
            c = scan();
            double m = 1;
            while (!isWhiteSpace(c)) {
                if (c == 'e' || c == 'E') {
                    return res * Math.pow(10, scanInt());
                }
                m /= 10;
                res += (c - '0') * m;
                c = scan();
            }
        }
        return res * sgn;
    }

}

Vantaggi:

  • Scansione input più veloce di BufferReader
  • Riduce la complessità temporale
  • Flush Buffer per ogni input successivo

Metodi:

  • scanChar () - scansiona un singolo carattere
  • scanInt () - scan Valore intero
  • scanLong () - scan Long value
  • scanString () - scan Valore stringa
  • scanDouble () - scan Double value
  • scanInt (int [] array) - esegue la scansione dell'array completo (intero)
  • scanLong (long [] array) - esegue la scansione dell'array completo (Long)

Utilizzo:

  1. Copia il codice indicato sotto il codice java.
  2. Inizializza oggetto per una determinata classe

ScanReader sc = new ScanReader(System.in); 3. Importare le classi necessarie:

import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; 4. Generare IOException dal metodo principale per gestire l'eccezione 5. Utilizzare i metodi forniti. 6. Divertiti

Esempio :

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
class Main{
    public static void main(String... as) throws IOException{
        ScanReader sc = new ScanReader(System.in);
        int a=sc.scanInt();
        System.out.println(a);
    }
}
class ScanReader....

10

Per evitare il problema, utilizzare nextLine();subito dopo nextInt();poiché aiuta a svuotare il buffer. Quando si preme ENTERil tasto nextInt();non acquisisce la nuova riga e quindi salta il Scannercodice in un secondo momento.

Scanner scanner =  new Scanner(System.in);
int option = scanner.nextInt();
scanner.nextLine(); //clearing the buffer

9

sc.nextLine()è migliore rispetto all'analisi dell'input. Perché dal punto di vista delle prestazioni sarà buono.


5

Immagino di essere piuttosto in ritardo alla festa ..

Come precedentemente affermato, chiamare input.nextLine()dopo aver ottenuto il valore int risolverà il problema. Il motivo per cui il tuo codice non ha funzionato è perché non c'era nient'altro da memorizzare dal tuo input (dove hai inserito l'int) string1. Farò solo un po 'più di luce sull'intero argomento.

Considera nextLine () come quello dispari tra i metodi nextFoo () nella classe Scanner. Facciamo un rapido esempio. Supponiamo di avere due righe di codice come quelle qui sotto:

int firstNumber = input.nextInt();
int secondNumber = input.nextInt();

Se inseriamo il valore seguente (come una singola riga di input)

54 234

Il valore del nostro firstNumbere della secondNumbervariabile diventano rispettivamente 54 e 234. Il motivo per cui funziona in questo modo è perché un nuovo feed di riga ( ovvero \ n ) NON viene generato automaticamente quando il metodo nextInt () accetta i valori. Prende semplicemente il "prossimo int" e continua. Questo è lo stesso per il resto dei metodi nextFoo () tranne nextLine ().

nextLine () genera un nuovo feed di riga immediatamente dopo aver acquisito un valore; questo è ciò che @RohitJain intende dicendo che il nuovo feed di riga è "consumato".

Infine, il metodo next () prende semplicemente la stringa più vicina senza generare una nuova riga; questo rende questo il metodo preferenziale per prendere stringhe separate all'interno della stessa linea singola.

Spero che questo aiuti ... Buon codice!


che cos'è un "nuovo feed di riga"?
Archer

@Abcd Un nuovo feed di riga significa sostanzialmente "partire da una nuova riga".
Taslim Oseni,

1
Quell'esempio "54 234" chiarisce davvero le cose.
Alonso del Arte,

3

Utilizzare 2 oggetti scanner anziché uno

Scanner input = new Scanner(System.in);
System.out.println("Enter numerical value");    
int option;
Scanner input2 = new Scanner(System.in);
option = input2.nextInt();

2
public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int i = scan.nextInt();
        scan.nextLine();
        double d = scan.nextDouble();
        scan.nextLine();
        String s = scan.nextLine();

        System.out.println("String: " + s);
        System.out.println("Double: " + d);
        System.out.println("Int: " + i);
    }

Se si utilizza il metodo nextLine () immediatamente dopo il metodo nextInt (), ricordare che nextInt () legge i token interi; per questo motivo, l'ultimo carattere di nuova riga per quella riga di input di numeri interi è ancora in coda nel buffer di input e il prossimo nextLine () leggerà il resto della riga di numeri interi (che è vuota).
Neeraj Gahlawat,

2

se mi aspetto un input non vuoto

public static Function<Scanner,String> scanLine = (scan -> {
    String s = scan.nextLine();
    return( s.length() == 0 ? scan.nextLine() : s );
  });


usato nell'esempio sopra:

System.out.println("Enter numerical value");    
int option = input.nextInt(); // read numerical value from input

System.out.println("Enter 1st string"); 
String string1 = scanLine.apply( input ); // read 1st string
System.out.println("Enter 2nd string");
String string2 = scanLine.apply( input ); // read 2nd string

2

In uno dei miei casi d'uso, ho avuto lo scenario di leggere un valore di stringa preceduto da un paio di valori interi . Ho dovuto usare un ciclo " for / while " per leggere i valori. E nessuno dei suggerimenti sopra ha funzionato in questo caso.

Utilizzando input.next()invece di input.nextLine()risolto il problema. Spero che questo possa essere utile per chi ha a che fare con uno scenario simile.


2

Usa questo codice risolverà il tuo problema.

System.out.println("Enter numerical value");    
int option;
option = input.nextInt(); // Read numerical value from input
input.nextLine();
System.out.println("Enter 1st string"); 
String string1 = input.nextLine(); // Read 1st string (this is skipped)
System.out.println("Enter 2nd string");
String string2 = input.nextLine(); // Read 2nd string (this appears right after reading numerical value)

2

Poiché i nextXXX()metodi non leggono newline, tranne nextLine(). Possiamo saltare il newlinedopo aver letto qualsiasi non-stringvalore ( intin questo caso) usando scanner.skip()come di seguito:

Scanner sc = new Scanner(System.in);
int x = sc.nextInt();
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(x);
double y = sc.nextDouble();
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(y);
char z = sc.next().charAt(0);
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(z);
String hello = sc.nextLine();
System.out.println(hello);
float tt = sc.nextFloat();
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(tt);

0

Perché non utilizzare un nuovo scanner per ogni lettura? Come sotto. Con questo approccio non dovrai affrontare il tuo problema.

int i = new Scanner(System.in).nextInt();

3
Ma devi chiudere il Scannerper impedire la perdita di memoria. Tempo perso?
TheCoffeeCup

1
Il GC non se ne occupa se lo scanner è avvolto in un metodo? Come: String getInput () {return new Scanner (System.in) .nextLine ()};
Tobias Johansson,

Questo è assolutamente errato. nextInt()non consumerà la nuova riga, indipendentemente dal fatto che sia in una "nuova" Scannero già utilizzata.
Dawood ibn Kareem,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.