Der Scanner überspringt nextLine (), nachdem next () oder nextFoo () verwendet wurden.


684

Ich benutze die ScannerMethoden nextInt()und nextLine()zum Lesen von Eingaben.

Es sieht aus wie das:

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)

Das Problem ist, dass nach Eingabe des numerischen Werts der erste input.nextLine()übersprungen und der zweite input.nextLine()ausgeführt wird, sodass meine Ausgabe folgendermaßen aussieht:

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

Ich habe meine Anwendung getestet und es sieht so aus, als ob das Problem in der Verwendung liegt input.nextInt(). Wenn ich es lösche, werden beide string1 = input.nextLine()und string2 = input.nextLine()so ausgeführt, wie ich es möchte.


5
Oder du könntest wie ich sein und BufferedReader verwenden :) Es ist mir egal, ob es alte Schule ist, es hat immer funktioniert und wird immer für mich funktionieren. Das Wissen über BufferedReader kann auch an anderer Stelle angewendet werden. Ich mag Scanner einfach nicht.
Cruncher

Antworten:


831

Dies liegt daran, dass die Scanner.nextIntMethode das Zeilenumbruchzeichen in Ihrer Eingabe nicht liest, das durch Drücken der Eingabetaste erstellt wurde, und daher der Aufruf von Scanner.nextLinenach dem Lesen dieser Zeilenumbruch zurückkehrt .

Sie werden auf ein ähnliches Verhalten stoßen, wenn Sie Scanner.nextLineafter Scanner.next()oder eine andere Scanner.nextFooMethode (außer sich nextLineselbst) verwenden.

Problemumgehung:

  • Setzen entweder einen Scanner.nextLineAnruf nach jedem Scanner.nextIntoder Scanner.nextFoozu konsumieren Rest der Zeile einschließlich Newline

    int option = input.nextInt();
    input.nextLine();  // Consume newline left-over
    String str1 = input.nextLine();
  • Oder, noch besser, lesen Sie die Eingabe durch Scanner.nextLineund konvertieren Sie Ihre Eingabe in das richtige Format, das Sie benötigen. Beispielsweise können Sie mithilfe der Integer.parseInt(String)Methode in eine Ganzzahl konvertieren .

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

4
@blekione. Sie müssen verwenden try-catch, weil Integer.parseIntwirft, NumberFormatExceptionwenn ein ungültiges Argument an es übergeben wird. Sie werden später etwas über Ausnahmen erfahren. Für EG: - Integer.parseInt("abc"). Sie möchten nicht, dass "abc" in int konvertiert wird, oder?
Rohit Jain

3
@blekione. Im obigen Fall wird Ihr Code an diesem Punkt angehalten und Sie können die Ausführung nicht fortsetzen. Mit der Ausnahmebehandlung können Sie mit solchen Bedingungen umgehen.
Rohit Jain

4
Inwieweit ist letzteres besser? AFAIK Scanner # nextInt () ist viel milder beim Finden korrekter Ints, indem Gruppenkommas sowie Gebietsschemapräfixe und -suffixe zugelassen werden. Die Ganzzahl # parseInt () erlaubt nur Ziffern und Dezimalstellen sowie ein optionales Vorzeichen.
Mordechai

5
Ich persönlich bevorzuge einen Scanner#hasNextFoovorherigen Scheck anstelle eines Try-Catch, aber das funktioniert auch.
MildlyMilquetoast

1
Use ParseDouble hat ein neues Problem: nextDouble verwendet die regionale Konfiguration von Dezimal (. Oder,), Parsedouble erhält jedoch immer US-Dezimal (.).
Olmerg

209

Das Problem ist mit dem Methode input.nextInt () , die nur den Wert int liest. Wenn Sie also mit input.nextLine () weiterlesen, erhalten Sie die Eingabetaste "\ n". Um dies zu überspringen, müssen Sie die input.nextLine () hinzufügen . Hoffe das sollte jetzt klar sein.

Versuchen Sie es so:

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 scheint eine Lösung zu sein, die die übersprungene Zeile nextLine
unbrauchbar macht


Frage: gefragt 27. Oktober 12 um 16:37. antwortete am 14. August 11 um 12:24 Uhr. Wie ist das überhaupt möglich?
Apoorv Patne

79

Wenn Sie eine Nummer eingeben und dann drücken Enter, wird input.nextInt()nur die Nummer verbraucht, nicht das "Zeilenende". Bei der input.nextLine()Ausführung wird das noch im Puffer befindliche "Zeilenende" von der ersten Eingabe verbraucht.

Verwenden Sie stattdessen input.nextLine()sofort danachinput.nextInt()


7
@ Victor es ist kein Fehler. es funktioniert wie angegeben. Sie könnten jedoch argumentieren, dass es eine einfache Möglichkeit geben sollte, die API anzuweisen, nach der Zieleingabe auch Leerzeichen zu verwenden.
Böhmisch

Aha. danke @Bohemian, genau das argumentiere ich. Ich denke, dass dies geändert werden sollte. Vielleicht können Sie mir zeigen, wo ich dieses "Problem" im nächsten JCP vorschlagen soll.
Victor

@victor Auf Wunsch erhalten Sie (und finden Sie heraus , wie Sie Code beitragen) über die ein Feature Berichten Sie einen Fehler und Feature Request Seite.
Böhmisch


48

Es scheint viele Fragen zu diesem Thema zu geben java.util.Scanner. Ich denke, eine besser lesbare / idiomatische Lösung wäre, aufzurufen scanner.skip("[\r\n]+"), um nach dem Aufruf alle Zeilenumbrüche zu löschen nextInt().

BEARBEITEN: Wie unten bei @PatrickParker angegeben, führt dies zu einer Endlosschleife, wenn der Benutzer nach der Nummer ein Leerzeichen eingibt. In der Antwort finden Sie ein besseres Muster für das Überspringen: https://stackoverflow.com/a/42471816/143585



Ich weiß, was wir tun, um die Daten im Puffer zu entfernen, aber in diesem Fall helfen Sie mir bitte dabei: stackoverflow.com/questions/33585314/having-issues-with-scanner
Khuong

1
Zu Ihrer Information, dies führt zu einer Endlosschleife, wenn der Benutzer nach der numerischen Eingabe alle Tabulatoren oder Leerzeichen eingibt. Siehe meine Antwort hier für einen besseren Sprung: stackoverflow.com/a/42471816/7098259
Patrick Parker

@PatrickParker: Es verursacht tatsächlich eine Endlosschleife! Danke, habe die Antwort bearbeitet, um auf deine zu verlinken.
Denis Tulskiy

33

input.nextInt();Dies geschieht, weil der Zeilenumbruch nicht erfasst wird. Sie könnten wie die anderen vorgeschlagenen tun, indem Sie eine input.nextLine();darunter hinzufügen .
Alternativ können Sie es im C # -Stil tun und eine nextLine in eine Ganzzahl wie folgt analysieren:

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

Dies funktioniert genauso gut und spart Ihnen eine Codezeile.



1
Sie müssen hier try catch verwenden. Was passiert, wenn die Eingabe keine Zahl ist? NumberFormatException muss hier behandelt werden.
Arundev

Dies setzt voraus, dass in jeder Zeile, für die Sie dies versuchen, nur ein int-Token vorhanden ist.
Cellepo

25

TL; DR Verwendung scanner.skip("\\R")vor jedem scanner.newLine()Aufruf, der ausgeführt wird nach:

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

Dinge, die Sie wissen müssen:

  • Text, der nur wenige Zeilen darstellt, enthält auch nicht druckbare Zeichen zwischen Zeilen (wir nennen sie Zeilentrennzeichen) wie

    • Wagenrücklauf (CR - in String-Literalen dargestellt als "\r" )
    • Zeilenvorschub (LF - in String-Literalen dargestellt als "\n")
  • Wenn Sie Daten von der Konsole lesen, kann der Benutzer seine Antwort eingeben, und wenn er fertig ist, muss er dies irgendwie tun diese Tatsache bestätigen. Dazu muss der Benutzer die Eingabetaste / Eingabetaste auf der Tastatur drücken.

    Wichtig ist, dass dieser Schlüssel neben der Sicherstellung, dass Benutzerdaten in die Standardeingabe (dargestellt durch System.indie gelesen wird Scanner) gestellt werden, auch betriebssystemabhängige Zeilentrennzeichen (wie bei Windows \r\n) nachträglich sendet .

    Wenn Sie also den Benutzer nach einem Wert wie fragen ageund Benutzertypen 42 eingeben und die Eingabetaste drücken, enthält die Standardeingabe "42\r\n".

Problem

Scanner#nextInt(und andere Methoden) erlaubt es nicht , Scanner zu verbrauchen diese Zeilenseparatoren. Es wird sich von lesen (wie sonst Scanner würde wissen , dass es nicht mehr Ziffern von dem Benutzer , die repräsentieren Wert als Leerzeichen gegenüber ?) , Die sie von der Standardeingabe entfernen, aber es wird auch cachen diese Zeilenseparatoren intern . Wir müssen uns daran erinnern, dass alle Scannermethoden immer ausgehend vom zwischengespeicherten Text scannen. Scanner#nextTypeSystem.inage

Jetzt werden Scanner#nextLine()einfach alle Zeichen gesammelt und zurückgegeben, bis Zeilentrennzeichen (oder das Ende des Streams) gefunden werden. Da Zeilentrennzeichen nach dem Lesen der Nummer von der Konsole sofort im Cache des Scanners gefunden werden, wird eine leere Zeichenfolge zurückgegeben, was bedeutet, dass Scanner vor diesen Zeilentrennzeichen (oder dem Ende des Streams) kein Zeichen finden konnte.
BTW nextLineauch verbraucht diese Zeilenseparatoren.

Lösung

Also , wenn Sie wollen Nummer fragen und dann für ganze Zeile während der leeren Zeichenfolge als Ergebnis zu vermeiden nextLine, entweder

  • verbrauchen Zeilentrennzeichen nextIntvon Scanner-Cache von
    • anrufen nextLine,
    • oder IMO besser lesbar wäre, wenn Sie anrufen skip("\\R")oder skip("\r\n|\r|\n")den Scanner überspringen lassen, der mit einem Zeilentrennzeichen übereinstimmt (weitere Informationen zu \R: https://stackoverflow.com/a/31060125 )
  • Verwenden Sie überhaupt keine Methoden nextInt( nextoder nextTYPEMethoden). Lesen Sie stattdessen die gesamten Daten Zeile für Zeile mit nextLineund analysieren Sie die Nummern aus jeder Zeile (vorausgesetzt, eine Zeile enthält nur eine Nummer), um den richtigen Typ wie intvia zu erhalten Integer.parseInt.

Übrigens : Methoden können Trennzeichen (standardmäßig alle Leerzeichen wie Tabulatoren, Zeilentrennzeichen) einschließlich der vom Scanner zwischengespeicherten Trennzeichen überspringen , bis sie den nächsten Nicht-Trennzeichenwert (Token) finden. Vielen Dank für die Eingabe wie CodeScanner#nextType"42\r\n\r\n321\r\n\r\n\r\nfoobar"

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

wird in der Lage sein, richtig zuzuweisen num1=42 num2=321 name=foobar.


14

Anstelle der input.nextLine()Verwendung input.next()sollte dies das Problem lösen.

Geänderter Code:

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

Wenn Sie sowohl Zeichenfolgen als auch Ints lesen möchten, können Sie zwei Scanner verwenden:

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
Das habe ich schon seit einiger Zeit gesucht. Kluger Zug!
Liam Wilson

1
@BdLearner scanner.nextLine()sollte funktionieren.
André Valenti

11

Wenn Sie Eingaben schnell scannen möchten, ohne in die nextLine () -Methode der Scannerklasse verwirrt zu werden, verwenden Sie dafür den benutzerdefinierten Eingabescanner.

Code:

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;
    }

}

Vorteile:

  • Scannt die Eingabe schneller als BufferReader
  • Reduziert die Zeitkomplexität
  • Leert den Puffer für jede nächste Eingabe

Methoden:

  • scanChar () - Einzelzeichen scannen
  • scanInt () - Ganzzahligen Wert scannen
  • scanLong () - Langwert scannen
  • scanString () - Wert der Scanzeichenfolge
  • scanDouble () - Doppelwert scannen
  • scanInt (int [] array) - scannt das gesamte Array (Integer)
  • scanLong (long [] array) - scannt das gesamte Array (Long)

Verwendungszweck :

  1. Kopieren Sie den angegebenen Code unter Ihren Java-Code.
  2. Objekt für bestimmte Klasse initialisieren

ScanReader sc = new ScanReader(System.in); 3. Importieren Sie die erforderlichen Klassen:

import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; 4. Lösen Sie IOException von Ihrer Hauptmethode aus, um Exception zu behandeln. 5. Verwenden Sie Provided Methods. 6. Genießen Sie

Beispiel:

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

Um das Problem zu vermeiden, verwenden Sie es nextLine();sofort danach, nextInt();da es beim Löschen des Puffers hilft. Wenn Sie drücken ENTERdie nextInt();nicht die neue Linie erfassen und daher überspringt die Scannerspäter Code.

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

9

sc.nextLine()ist besser als das Parsen der Eingabe. Weil es in Bezug auf die Leistung gut sein wird.


5

Ich denke ich bin ziemlich spät zur Party ..

Wie bereits erwähnt, input.nextLine()löst ein Aufruf nach dem Abrufen Ihres int-Werts Ihr Problem. Der Grund, warum Ihr Code nicht funktionierte, war, dass Sie nichts anderes von Ihrer Eingabe (in die Sie das int eingegeben haben) speichern konnten string1. Ich werde nur ein wenig mehr Licht auf das gesamte Thema werfen.

Betrachten Sie nextLine () als die ungerade unter den nextFoo () -Methoden in der Scanner-Klasse. Nehmen wir ein kurzes Beispiel. Nehmen wir an, wir haben zwei Codezeilen wie die folgenden:

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

Wenn wir den folgenden Wert eingeben (als einzelne Eingabezeile)

54 234

Der Wert von our firstNumberund secondNumbervariable wird zu 54 bzw. 234. Der Grund, warum dies so funktioniert, ist, dass ein neuer Zeilenvorschub ( dh \ n ) NICHT automatisch generiert wird, wenn die nextInt () -Methode die Werte aufnimmt. Es dauert einfach das "nächste int" und geht weiter. Dies gilt auch für die übrigen nextFoo () -Methoden mit Ausnahme von nextLine ().

nextLine () generiert unmittelbar nach der Eingabe eines Werts einen neuen Zeilenvorschub. Dies ist, was @RohitJain bedeutet, wenn gesagt wird, dass der neue Zeilenvorschub "verbraucht" ist.

Schließlich nimmt die next () -Methode einfach den nächsten String ohne eine neue Zeile generieren. Dies macht dies zur bevorzugten Methode, um separate Strings innerhalb derselben einzelnen Zeile zu erstellen.

Ich hoffe das hilft .. Frohe Codierung!


Was ist ein "neuer Zeilenvorschub"?
Archer

@Abcd Ein neuer Zeilenvorschub bedeutet im Grunde "von einer neuen Zeile ausgehen".
Taslim Oseni

1
Dieses Beispiel "54 234" verdeutlicht die Dinge wirklich.
Alonso del Arte

3

Verwenden Sie 2 Scannerobjekte anstelle von einem

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);
    }

Wenn Sie die nextLine () -Methode unmittelbar nach der nextInt () -Methode verwenden, erinnern Sie sich daran, dass nextInt () ganzzahlige Token liest. Aus diesem Grund befindet sich das letzte Zeilenumbruchzeichen für diese Zeile der Ganzzahleingabe immer noch im Eingabepuffer in der Warteschlange, und die nächste nextLine () liest den Rest der Ganzzahlzeile (die leer ist).
Neeraj Gahlawat

2

wenn ich eine nicht leere Eingabe erwarte

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


im obigen Beispiel verwendet:

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 einem meiner Anwendungsfälle hatte ich das Szenario, einen Zeichenfolgenwert zu lesen , dem einige ganzzahlige Werte vorangestellt waren . Ich musste eine " for / while-Schleife " verwenden, um die Werte zu lesen. Und keiner der oben genannten Vorschläge hat in diesem Fall funktioniert.

Verwenden input.next()statt input.nextLine()beheben das Problem. Ich hoffe, dies könnte für diejenigen hilfreich sein, die sich mit einem ähnlichen Szenario befassen.


2

Verwenden Sie diesen Code, um Ihr Problem zu beheben.

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

Da nextXXX()Methoden nicht lesen newline, außer nextLine(). Wir können das newlinenach dem Lesen eines beliebigen non-stringWertes ( intin diesem Fall) überspringen, indem wir scanner.skip()Folgendes verwenden:

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

Warum nicht für jede Lesung einen neuen Scanner verwenden? Wie unten. Mit diesem Ansatz werden Sie Ihr Problem nicht konfrontieren.

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

3
Aber dann müssen Sie die schließen Scanner, um Speicherverlust zu verhindern. Zeit verschwenden?
TheCoffeeCup

1
Kümmert sich der GC nicht darum, wenn der Scanner in eine Methode eingebunden ist? Wie: String getInput () {neuen Scanner zurückgeben (System.in) .nextLine ()};
Tobias Johansson

Das ist absolut falsch. nextInt()verbraucht die Newline nicht, unabhängig davon, ob es sich um eine "neue" Scanneroder eine bereits verwendete handelt.
Dawood ibn Kareem
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.