Skaner pomija nextLine () po użyciu next () lub nextFoo ()?

684

Korzystam z Scannermetod nextInt()i nextLine()do odczytu danych wejściowych.

To wygląda tak:

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)

Problem polega na tym, że po wprowadzeniu wartości liczbowej pierwsza input.nextLine()jest pomijana, a druga input.nextLine()wykonywana, dzięki czemu moje dane wyjściowe wyglądają następująco:

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

Przetestowałem moją aplikację i wygląda na to, że problem polega na użyciu input.nextInt(). Gdybym go usunąć, a następnie oba string1 = input.nextLine()i string2 = input.nextLine()są wykonywane jak chcę je mieć.

blekione
źródło
5
Albo możesz być taki jak ja i używać BufferedReadera :) Nie obchodzi mnie, czy to stara szkoła, zawsze działała i zawsze będzie działać dla mnie. Ponadto znajomość BufferedReader ma zastosowanie gdzie indziej. Po prostu nie lubię skanera.
Cruncher

Odpowiedzi:

831

Jest tak, ponieważ Scanner.nextIntmetoda nie odczytuje znaku nowego wiersza w danych wejściowych utworzonych przez naciśnięcie klawisza „Enter”, a więc wywołanie Scanner.nextLinepowrotu po przeczytaniu tego nowego wiersza .

Będzie można napotkać podobne zachowanie podczas korzystania Scanner.nextLinepo Scanner.next()lub dowolny Scanner.nextFoosposób (oprócz nextLinesiebie).

Obejście:

  • Albo umieścić Scanner.nextLinepołączenia po każdym Scanner.nextIntlub Scanner.nextFoozużywają resztę tej linii w tym nowej linii

    int option = input.nextInt();
    input.nextLine();  // Consume newline left-over
    String str1 = input.nextLine();
    
  • Lub jeszcze lepiej, przeczytaj dane wejściowe Scanner.nextLinei przekonwertuj dane wejściowe do odpowiedniego formatu, którego potrzebujesz. Na przykład możesz przekonwertować na liczbę całkowitą za pomocą Integer.parseInt(String)metody.

    int option = 0;
    try {
        option = Integer.parseInt(input.nextLine());
    } catch (NumberFormatException e) {
        e.printStackTrace();
    }
    String str1 = input.nextLine();
    
Rohit Jain
źródło
4
@blekione. Musisz użyć try-catch, ponieważ Integer.parseIntrzuca, NumberFormatExceptiongdy zostanie przekazany nieprawidłowy argument. Później dowiesz się o wyjątku. Dla EG: - Integer.parseInt("abc"). Nie chcesz, aby „abc” zostało przekonwertowane na int, prawda?
Rohit Jain
3
@blekione. W powyższym przypadku kod zostanie zatrzymany w tym momencie i nie będzie można kontynuować wykonywania. Dzięki obsłudze wyjątków możesz poradzić sobie z tego rodzaju warunkami.
Rohit Jain
4
W jakim stopniu ten ostatni jest lepszy? Skaner AFAIK # nextInt () jest o wiele łagodniejszy w wyszukiwaniu poprawnych liczb całkowitych, zezwalając na przecinki grupowe oraz prefiksy i sufiksy ustawień regionalnych. Liczba całkowita # parseInt () dopuszcza tylko cyfry i przecinek dziesiętny oraz znak opcjonalny.
Mordechai,
5
Osobiście wolę Scanner#hasNextFooczek wcześniej niż try-catch, ale to też działa.
MildlyMilquetoast
1
Użycie ParseDouble ma nowy problem, że nextDouble używa konfiguracji regionalnej dziesiętnej (. Lub,), ale Parsedouble zawsze odbiera dziesiętną wartość US (.).
olmerg
209

Problem dotyczy metody input.nextInt () - odczytuje tylko wartość int. Więc kiedy będziesz kontynuować czytanie za pomocą input.nextLine (), otrzymasz klawisz „\ n” Enter. Aby to pominąć, musisz dodać input.nextLine () . Mam nadzieję, że to powinno być teraz jasne.

Spróbuj tak:

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();
Prine
źródło
umm wydaje się rozwiązaniem, dzięki czemu pomijana linia nie jest używana nextLine, ale wciąż potrzebuję wyjaśnienia tego zachowania
inż. Fouad
2
FYI: scalono ze stackoverflow.com/questions/7056749/...
Shog9,
Pytanie: zadane 27 października 2012 o 16:37. odpowiedział 14 sierpnia 11 o 12:24. Jak to w ogóle możliwe?
Apoorv Patne
79

Dzieje się tak dlatego, że po wprowadzeniu liczby, a następnie naciśnięciu Enter, input.nextInt()zużywa tylko liczbę, a nie „koniec linii”. Kiedy jest input.nextLine()wykonywany, zużywa „koniec linii” nadal w buforze od pierwszego wejścia.

Zamiast tego użyj input.nextLine()natychmiast poinput.nextInt()

Czeski
źródło
7
@Victor to nie błąd. działa zgodnie z opisem. można jednak argumentować, że powinien istnieć łatwy sposób, aby poinformować interfejs API, aby wykorzystywał dowolne spacje po wprowadzeniu danych docelowych.
Czeski
Widzę. dzięki @ Bohemian, właśnie o to się kłócę. Myślę, że należy to zmienić, być może możesz wskazać mi, gdzie zasugerować ten „problem” w następnym JCP.
Victor
@victor Możesz poprosić (i dowiedzieć się, jak przekazać kod) funkcję za pośrednictwem strony Zgłoś błąd lub Poproś o funkcję .
Czeski
48

Wydaje się, że jest wiele pytań na ten temat java.util.Scanner. Myślę, że bardziej czytelnym / idiomatycznym rozwiązaniem byłoby wywołanie, scanner.skip("[\r\n]+")aby usunąć znaki nowej linii po wywołaniu nextInt().

EDYCJA: jak zauważono poniżej @PatrickParker, spowoduje to nieskończoną pętlę, jeśli użytkownik wprowadzi dowolną spację po liczbie. Zobacz ich odpowiedź na lepszy wzór do użycia z pominięciem: https://stackoverflow.com/a/42471816/143585

Denis Tulskiy
źródło
Wiem, co robimy, aby usunąć dane z bufora, ale w tym przypadku pomóż mi w tym: stackoverflow.com/questions/33585314/having-issues-with-scanner
Khuong
1
Dla twojej informacji spowoduje to nieskończoną pętlę, gdy użytkownik wpisze dowolne tabulatory lub spacje po wprowadzeniu numerycznym. Zobacz moją odpowiedź tutaj dla lepszego pominięcia: stackoverflow.com/a/42471816/7098259
Patrick Parker
@PatrickParker: to rzeczywiście powoduje nieskończoną pętlę! Dzięki, zredagowałem odpowiedź na link do twojego.
Denis Tulskiy
33

Robi to, ponieważ input.nextInt();nie przechwytuje nowej linii. możesz zrobić tak, jak inne zaproponowane przez dodanie input.nextLine();spodu.
Alternatywnie możesz to zrobić w stylu C # i parsować nextLine na liczbę całkowitą w następujący sposób:

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

Wykonanie tego działa równie dobrze i pozwala zaoszczędzić wiersz kodu.

Kawa elektryczna
źródło
1
musisz użyć try catch tutaj. Co się stanie, jeśli wejście nie będzie liczbą. NumberFormatException należy obsługiwać tutaj.
Arundev
Zakłada się, że na każdej linii, dla której próbujesz, jest tylko jeden token int.
cellepo
25

TL; DR Użyj scanner.skip("\\R")przed każdym scanner.newLine()połączeniem, które jest wykonywane po:

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

Co musisz wiedzieć:

  • tekst reprezentujący kilka wierszy zawiera także znaki niedrukowalne między wierszami (nazywamy je separatorami wierszy), jak

    • powrót karetki (CR - w literałach łańcuchowych reprezentowanych jako "\r")
    • znak wiersza (LF - w literałach łańcuchowych reprezentowanych jako "\n")
  • kiedy odczytujesz dane z konsoli, użytkownik może wpisać swoją odpowiedź, a kiedy skończy, musi jakoś potwierdzić ten fakt. Aby to zrobić, użytkownik musi nacisnąć klawisz „enter” / „return” na klawiaturze.

    Ważne jest to, że ten klucz, oprócz zapewnienia umieszczenia danych użytkownika na standardowym wejściu (reprezentowanym przez, System.inktóry jest czytany przez Scanner), wysyła również \r\npo nim separatory linii zależne od systemu operacyjnego (jak w przypadku systemu Windows ).

    Więc kiedy pytasz użytkownika o wartość podobną age, a typy użytkowników 42 i prasy wchodzą, standardowe wejście będzie zawierać "42\r\n".

Problem

Scanner#nextInt(i inne metody) nie pozwalają Scanner zużywają tych separatorów. Odczyta je (skąd inaczej skaner wiedziałby, że nie ma więcej cyfr od użytkownika, które reprezentują wartość niż stojące przed białymi spacjami?), Co usunie je ze standardowego wejścia, ale również buforuje te separatory linii wewnętrznie . Musimy pamiętać, że wszystkie metody skanera zawsze skanują, zaczynając od buforowanego tekstu. Scanner#nextTypeSystem.inage

Teraz Scanner#nextLine()po prostu zbiera i zwraca wszystkie znaki, aż znajdzie separatory linii (lub koniec strumienia). Ale ponieważ separatory linii po odczytaniu numeru z konsoli znajdują się natychmiast w pamięci podręcznej skanera, zwraca pusty ciąg znaków, co oznacza, że ​​skaner nie był w stanie znaleźć żadnego znaku przed tymi separatorami linii (lub końcem strumienia).
BTW zużywanextLine również te separatory linii.

Rozwiązanie

Więc jeśli chcesz zapytać o liczbę, a następnie o całą linię, unikając w wyniku tego pustego ciągu znaków nextLine, albo

  • zużywa separator linii pozostawiony przez nextIntpamięć podręczną skanerów przez
    • dzwoni nextLine,
    • lub IMO bardziej czytelnym sposobem byłoby zadzwonienie skip("\\R")lub skip("\r\n|\r|\n")umożliwienie skanerowi pominięcia części dopasowanej przez separator linii (więcej informacji na temat \R: https://stackoverflow.com/a/31060125 )
  • w ogóle nie używaj nextInt(ani nextżadnych nextTYPEmetod). Zamiast tego należy czytać całe dane linia po linii za pomocą nextLinei analizować liczby z każdej linii (zakładając, że jedna linia zawiera tylko jedną liczbę) do odpowiedniego typu, np . intVia Integer.parseInt.

BTW : metody mogą pomijać separatory (domyślnie wszystkie białe spacje, takie jak tabulatory, separatory linii), w tym te buforowane przez skaner, dopóki nie znajdą następnej wartości innej niż separator (token). Dzięki temu za wprowadzanie jak kodScanner#nextType"42\r\n\r\n321\r\n\r\n\r\nfoobar"

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

będzie mógł poprawnie przypisać num1=42 num2=321 name=foobar.

Pshemo
źródło
14

Zamiast input.nextLine()stosowania input.next(), które powinny rozwiązać ten problem.

Zmodyfikowany kod:

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();
}
Castaldi
źródło
13

Jeśli chcesz odczytać zarówno ciągi, jak i ints, rozwiązaniem jest użycie dwóch skanerów:

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();
André Valenti
źródło
1
Tego właśnie szukałem od jakiegoś czasu. Mądry ruch!
Liam Wilson
1
@BdLearner scanner.nextLine()powinien działać.
André Valenti
11

Jeśli chcesz szybko skanować dane wejściowe bez pomyłek w metodzie klasy NextLine () klasy Scanner, użyj do tego niestandardowego skanera wejściowego.

Kod :

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

}

Zalety :

  • Skanuje dane wejściowe szybciej niż BufferReader
  • Zmniejsza złożoność czasu
  • Opróżnia bufor dla każdego następnego wejścia

Metody:

  • scanChar () - skanuje pojedynczy znak
  • scanInt () - skanuje wartość całkowitą
  • scanLong () - skanowanie Długa wartość
  • scanString () - skan Wartość ciągu
  • scanDouble () - skanowanie Podwójna wartość
  • scanInt (int [] array) - skanuje pełny Array (Integer)
  • scanLong (long [] array) - skanuje pełny Array (Long)

Stosowanie :

  1. Skopiuj podany kod poniżej swojego kodu Java.
  2. Zainicjuj obiekt dla danej klasy

ScanReader sc = new ScanReader(System.in); 3. Zaimportuj niezbędne klasy:

import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; 4. Rzuć wyjątek IOException od swojej głównej metody obsługi wyjątku 5. Użyj dostarczonych metod. 6. Ciesz się

Przykład:

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....
NIKUNJ KHOKHAR
źródło
10

Aby uniknąć tego problemu, użyj nextLine();natychmiast po tym, nextInt();ponieważ pomaga wyczyścić bufor. Po naciśnięciu nie uchwycić nową linię, a więc pomija kod później.ENTERnextInt();Scanner

Scanner scanner =  new Scanner(System.in);
int option = scanner.nextInt();
scanner.nextLine(); //clearing the buffer
Urvashi Gupta
źródło
9

sc.nextLine()jest lepszy w porównaniu do analizowania danych wejściowych. Ponieważ pod względem wydajności będzie dobrze.

Shankar upadhyay
źródło
5

Chyba spóźniłem się na imprezę ...

Jak już wspomniano, wywołanie input.nextLine()po uzyskaniu wartości int rozwiąże problem. Powodem, dla którego twój kod nie działał, było to, że nie było nic innego do zapamiętania z danych wejściowych (gdzie wpisałeś int) string1. Rzucę trochę więcej światła na cały temat.

Rozważ nextLine () jako nieparzystą spośród metod nextFoo () w klasie Scanner. Weźmy szybki przykład. Powiedzmy, że mamy dwa wiersze kodu, takie jak poniższe:

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

Jeśli wprowadzimy wartość poniżej (jako pojedynczy wiersz danych wejściowych)

54 234

Wartość naszych firstNumberi secondNumberzmiennych wynosi odpowiednio 54 i 234. Powodem, dla którego działa to w ten sposób, jest to, że nowy wysuw wiersza ( tj. \ N ) NIE JEST generowany automatycznie, gdy metoda nextInt () przyjmuje wartości. Po prostu bierze „next int” i idzie dalej. To samo dotyczy pozostałych metod nextFoo (), z wyjątkiem nextLine ().

nextLine () generuje nowy wiersz informacji natychmiast po przyjęciu wartości; to właśnie oznacza @RohitJain, mówiąc, że nowy wiersz jest „zużyty”.

Wreszcie, metoda next () po prostu pobiera najbliższy ciąg bez generowania nowej linii; sprawia to, że jest to preferowana metoda pobierania oddzielnych ciągów znaków w tej samej pojedynczej linii.

Mam nadzieję, że to pomoże .. Wesołego kodowania!

Taslim Oseni
źródło
co to jest „nowy wiersz informacji”?
Archer
@Abcd Nowy znak linii zasadniczo oznacza „zaczynać od nowej linii”.
Taslim Oseni
1
Ten przykład z „54 234” naprawdę wyjaśnia sprawy.
Alonso del Arte
3

Użyj 2 obiektów skanera zamiast jednego

Scanner input = new Scanner(System.in);
System.out.println("Enter numerical value");    
int option;
Scanner input2 = new Scanner(System.in);
option = input2.nextInt();
Surowy Szach
źródło
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);
    }
Neeraj Gahlawat
źródło
Jeśli użyjesz metody nextLine () bezpośrednio po metodzie nextInt (), pamiętaj, że nextInt () odczytuje tokeny całkowite; z tego powodu ostatni znak nowej linii dla tego wiersza wprowadzania liczb całkowitych jest nadal w kolejce w buforze wejściowym, a następny nextLine () będzie odczytywał resztę wiersza liczb całkowitych (który jest pusty).
Neeraj Gahlawat
2

jeśli oczekuję niepustego wejścia

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


użyte w powyższym przykładzie:

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
Kaplan
źródło
2

W jednym z moich przypadków użycia miałem scenariusz odczytu wartości ciągu poprzedzony kilkoma liczbami całkowitymi . Musiałem użyć „ pętli for / while ”, aby odczytać wartości. I żadna z powyższych sugestii nie zadziałała w tym przypadku.

Używanie input.next()zamiast input.nextLine()naprawionego problemu. Mam nadzieję, że może to być pomocne dla osób zajmujących się podobnym scenariuszem.

Giri
źródło
2

Użyj tego kodu, aby rozwiązać problem.

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)
Jeewantha Lahiru
źródło
2

Ponieważ nextXXX()metody nie czytają newline, z wyjątkiem nextLine(). Możemy pominąć newlinepo odczytaniu dowolnej non-stringwartości ( intw tym przypadku), wykonując scanner.skip()następujące czynności:

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);
Sandeep Kumar
źródło
0

Dlaczego nie użyć nowego skanera do każdego czytania? Jak poniżej Dzięki takiemu podejściu nie będziesz mieć do czynienia z problemem.

int i = new Scanner(System.in).nextInt();
Tobias Johansson
źródło
3
Ale musisz zamknąć, Scanneraby zapobiec wyciekom pamięci. Marnowanie czasu?
TheCoffeeCup
1
Czy GC nie zajmuje się tym, jeśli skaner jest zawinięty w metodę? Jak: String getInput () {return new Scanner (System.in) .nextLine ()};
Tobias Johansson
To jest absolutnie niepoprawne. nextInt()nie zużyje nowego wiersza, niezależnie od tego, czy znajduje się w „nowym”, Scannerczy już używanym.
Dawood ibn Kareem