Pemindai melewatkan nextLine () setelah menggunakan next () atau nextFoo ()?

684

Saya menggunakan Scannermetode nextInt()dan nextLine()untuk membaca input.

Ini terlihat seperti ini:

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)

Masalahnya adalah bahwa setelah memasukkan nilai numerik, yang pertama input.nextLine()dilewati dan yang kedua input.nextLine()dieksekusi, sehingga output saya terlihat seperti ini:

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

Saya menguji aplikasi saya dan sepertinya masalahnya terletak pada penggunaannya input.nextInt(). Jika saya menghapusnya, maka keduanya string1 = input.nextLine()dan string2 = input.nextLine()dieksekusi seperti yang saya inginkan.

blekione
sumber
5
Atau Anda bisa seperti saya dan menggunakan BufferedReader :) Saya tidak peduli apakah itu sekolah lama, selalu berhasil dan selalu akan bekerja untuk saya. Juga, pengetahuan tentang BufferedReader memiliki aplikasi di tempat lain. Saya tidak suka Scanner.
Cruncher

Jawaban:

831

Itu karena Scanner.nextIntmetode ini tidak membaca karakter baris baru di input Anda yang dibuat dengan menekan "Enter," dan panggilan untuk Scanner.nextLinekembali setelah membaca baris baru itu .

Anda akan menemukan perilaku serupa ketika Anda menggunakan Scanner.nextLinesetelah Scanner.next()atau Scanner.nextFoometode apa pun (kecuali nextLinedirinya sendiri).

Penanganan masalah:

  • Baik melakukan Scanner.nextLinepanggilan setelah masing Scanner.nextInt- masing atau Scanner.nextFoountuk mengkonsumsi sisa baris itu termasuk baris baru

    int option = input.nextInt();
    input.nextLine();  // Consume newline left-over
    String str1 = input.nextLine();
  • Atau, lebih baik lagi, baca input melalui Scanner.nextLinedan konversi input Anda ke format yang tepat yang Anda butuhkan. Misalnya, Anda dapat mengonversi ke integer menggunakan Integer.parseInt(String)metode.

    int option = 0;
    try {
        option = Integer.parseInt(input.nextLine());
    } catch (NumberFormatException e) {
        e.printStackTrace();
    }
    String str1 = input.nextLine();
Rohit Jain
sumber
4
@blekione. Anda harus menggunakan try-catch, karena Integer.parseIntmelempar NumberFormatExceptionketika argumen yang tidak valid diteruskan ke sana. Anda akan belajar tentang pengecualian nanti. Untuk EG: - Integer.parseInt("abc"). Anda tidak ingin "abc" dikonversi menjadi int, bukan?
Rohit Jain
3
@blekione. Jadi, dalam kasus di atas, kode Anda akan berhenti pada titik itu, dan Anda tidak akan dapat melanjutkan eksekusi. Dengan Penanganan Eksepsi, Anda dapat menangani kondisi seperti itu.
Rohit Jain
4
Sejauh mana yang terakhir lebih baik? Pemindai AFAIK # nextInt () jauh lebih lunak dalam menemukan int yang benar, dengan mengizinkan koma grup dan awalan dan sufiks lokal. Integer # parseInt () memungkinkan digit dan titik desimal hanya ditambah tanda opsional.
Mordechai
5
Saya pribadi lebih suka Scanner#hasNextFoocek sebelumnya daripada try-catch, tetapi itu juga berhasil.
MildlyMilquetoast
1
Gunakan ParseDouble memiliki masalah baru, bahwa nextDouble menggunakan konfigurasi regional desimal (. Atau,) tetapi Parsedouble selalu menerima desimal AS (.).
olmerg
209

Masalahnya adalah dengan metode input.nextInt () - hanya membaca nilai int. Jadi ketika Anda melanjutkan membaca dengan input.nextLine () Anda menerima tombol Enter "\ n". Jadi untuk melewati ini, Anda harus menambahkan input.nextLine () . Semoga ini harus jelas sekarang.

Cobalah seperti itu:

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
sumber
umm tampaknya solusi, membuat garis yang dilewati tidak digunakan nextLine, tapi saya masih perlu penjelasan tentang perilaku ini
Eng.Fouad
2
FYI: digabung dari stackoverflow.com/questions/7056749/…
Shog9
Pertanyaan: ditanyakan 27 Okt '12 pada jam 16:37. jawab 14 Agustus 11 pada 12:24. Bagaimana ini mungkin?
Apoorv Patne
79

Itu karena ketika Anda memasukkan nomor lalu tekan Enter, input.nextInt()hanya mengkonsumsi nomor, bukan "ujung jalur". Ketika input.nextLine()dijalankan, ia mengkonsumsi "end of line" yang masih ada di buffer dari input pertama.

Sebagai gantinya, gunakan input.nextLine()segera setelahnyainput.nextInt()

Bohemian
sumber
7
@ Viktor itu bukan bug. itu berfungsi seperti yang ditentukan. Anda bisa berpendapat bahwa seharusnya ada cara mudah untuk memberitahu api untuk juga mengkonsumsi spasi putih mengikuti input target.
Bohemian
Saya melihat. terima kasih @Bohemian, itulah tepatnya yang saya perdebatkan. Saya pikir ini harus diubah, mungkin Anda bisa menunjukkan di mana saya menyarankan "masalah" ini di JCP berikutnya.
Victor
@victor Anda dapat meminta (dan mencari tahu cara berkontribusi kode) suatu fitur melalui halaman Laporkan Bug atau Minta Fitur .
Bohemian
FYI: digabung dari stackoverflow.com/questions/7056749/…
Shog9
48

Tampaknya ada banyak pertanyaan tentang masalah ini java.util.Scanner. Saya pikir solusi yang lebih mudah dibaca / idiomatis adalah menelepon scanner.skip("[\r\n]+")untuk menjatuhkan karakter baris baru setelah menelepon nextInt().

EDIT: seperti yang dicatat oleh @PatrickParker di bawah ini, ini akan menyebabkan loop tak terbatas jika pengguna memasukkan spasi kosong setelah nomor. Lihat jawaban mereka untuk pola yang lebih baik untuk digunakan dengan lewati: https://stackoverflow.com/a/42471816/143585

Denis Tulskiy
sumber
FYI: digabung dari stackoverflow.com/questions/7056749/…
Shog9
Saya tahu apa yang kami lakukan untuk menghapus data dalam buffer, tetapi kasus ini, tolong bantu saya dengan ini: stackoverflow.com/questions/33585314/having-issues-with-scanner
Khuong
1
FYI, ini akan menyebabkan infinite loop adalah pengguna mengetik sembarang tab atau spasi setelah input numerik. Lihat jawaban saya di sini untuk lompatan yang lebih baik: stackoverflow.com/a/42471816/7098259
Patrick Parker
@ PatrickParker: itu memang menyebabkan infinite loop! Terima kasih, edit jawaban untuk menautkan ke milik Anda.
Denis Tulskiy
33

Itu karena input.nextInt();tidak menangkap baris baru. Anda dapat melakukan seperti yang lain yang diusulkan dengan menambahkan di input.nextLine();bawahnya.
Atau Anda dapat melakukannya dengan gaya C # dan mem-parsing nextLine ke integer seperti:

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

Melakukan ini juga berfungsi dengan baik, dan itu akan menyelamatkan Anda satu baris kode.

Kopi Listrik
sumber
FYI: digabung dari stackoverflow.com/questions/7056749/…
Shog9
1
Anda harus menggunakan coba tangkap di sini. Apa yang akan terjadi jika inputnya bukan angka. NumberFormatException perlu ditangani di sini.
Arundev
Ini mengasumsikan bahwa hanya ada satu token int pada setiap baris yang Anda coba ini.
cellepo
25

TL; DR Gunakan scanner.skip("\\R")sebelum setiap scanner.newLine()panggilan, yang dieksekusi setelah:

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

Hal-hal yang perlu Anda ketahui:

  • teks yang mewakili beberapa baris juga mengandung karakter yang tidak dapat dicetak antar baris (kami menyebutnya pemisah baris) seperti

    • carriage return (CR - dalam String literals diwakili sebagai "\r")
    • umpan baris (LF - dalam String literals diwakili sebagai "\n")
  • ketika Anda membaca data dari konsol, memungkinkan pengguna untuk mengetik jawabannya dan ketika dia selesai dia perlu entah bagaimana mengkonfirmasi fakta itu. Untuk melakukannya, pengguna harus menekan tombol "enter" / "return" pada keyboard.

    Yang penting adalah bahwa kunci ini selain memastikan menempatkan data pengguna ke input standar (diwakili oleh System.inyang dibaca Scanner) juga mengirimkan pemisah baris yang tergantung OS (seperti untuk Windows \r\n) setelahnya.

    Jadi, ketika Anda meminta nilai dari pengguna age, dan tipe pengguna 42 dan menekan enter, input standar akan berisi "42\r\n".

Masalah

Scanner#nextInt(dan metode lainnya ) tidak memungkinkan Pemindai untuk mengkonsumsi pemisah garis ini. Ini akan membacanya dari (bagaimana lagi Pemindai akan tahu bahwa tidak ada lebih banyak digit dari pengguna yang mewakili nilai daripada menghadap spasi?) Yang akan menghapusnya dari input standar, tetapi juga akan menembolok pemisah garis tersebut secara internal . Yang perlu kita ingat, adalah bahwa semua metode Pemindai selalu memindai mulai dari teks yang di-cache. Scanner#nextTypeSystem.inage

Sekarang Scanner#nextLine()cukup kumpulkan dan kembalikan semua karakter hingga menemukan pemisah garis (atau akhir aliran). Tetapi karena pemisah garis setelah membaca angka dari konsol ditemukan langsung di cache Pemindai, itu mengembalikan String kosong, yang berarti bahwa Pemindai tidak dapat menemukan karakter apa pun sebelum pemisah garis tersebut (atau ujung aliran).
BTW nextLinejuga mengkonsumsi pemisah garis tersebut.

Larutan

Jadi ketika Anda ingin meminta nomor dan kemudian untuk seluruh baris sambil menghindari string kosong sebagai akibatnya nextLine, baik

  • mengkonsumsi pemisah baris yang ditinggalkan oleh nextIntcache Pemindai oleh
    • memanggil nextLine,
    • atau IMO lebih mudah dibaca cara yang adalah dengan menelepon skip("\\R")atau skip("\r\n|\r|\n")membiarkan Pemindai melewati bagian yang cocok dengan pemisah baris (info lebih lanjut tentang \R: https://stackoverflow.com/a/31060125 )
  • jangan gunakan nextInt(atau next, atau apa punnextTYPE metode ) sama sekali. Alih-alih membaca seluruh data baris demi baris menggunakan nextLinedan mengurai angka dari setiap baris (dengan asumsi satu baris hanya berisi satu angka) ke tipe yang tepat seperti intvia Integer.parseInt.

BTW :Scanner#nextType metode dapat melewati pembatas (secara default semua spasi putih seperti tab, pemisah baris) termasuk yang di-cache oleh pemindai, sampai mereka akan menemukan nilai non-pembatas berikutnya (token). Berkat itu untuk input seperti "42\r\n\r\n321\r\n\r\n\r\nfoobar"kode

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

akan dapat menetapkan dengan benar num1=42 num2=321 name=foobar .

Pshemo
sumber
14

Alih-alih input.nextLine()digunakaninput.next() , itu seharusnya memecahkan masalah.

Kode yang dimodifikasi:

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
sumber
FYI: digabung dari stackoverflow.com/questions/7056749/…
Shog9
13

Jika Anda ingin membaca string dan int, solusinya adalah menggunakan dua Pemindai:

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
sumber
1
Inilah yang saya cari, untuk beberapa waktu sekarang. Langkah cerdas!
Liam Wilson
1
@BdLearner scanner.nextLine()harus bekerja.
André Valenti
11

Jika Anda ingin memindai input dengan cepat tanpa bingung dengan metode kelas Scanner nextLine (), Gunakan Custom Input Scanner untuk itu.

Kode:

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

}

Keuntungan :

  • Memindai Input lebih cepat dari BufferReader
  • Mengurangi Kompleksitas Waktu
  • Siram Buffer untuk setiap input selanjutnya

Metode:

  • scanChar () - memindai satu karakter
  • scanInt () - memindai nilai Integer
  • scanLong () - scan Nilai panjang
  • scanString () - memindai nilai String
  • scanDouble () - memindai nilai ganda
  • scanInt (int [] array) - memindai array lengkap (Integer)
  • scanLong (long [] array) - memindai Array lengkap (Panjang)

Penggunaan:

  1. Salin Kode Yang Diberikan di bawah kode java Anda.
  2. Inisialisasi Objek untuk Kelas yang Diberikan

ScanReader sc = new ScanReader(System.in); 3. Impor Kelas yang diperlukan:

import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; 4. Lempar IOException dari metode utama Anda untuk menangani Pengecualian 5. Gunakan Metode yang Disediakan. 6. Nikmati

Contoh:

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
sumber
10

Untuk menghindari masalah, gunakan nextLine();segera setelah nextInt();karena membantu dalam membersihkan buffer. Ketika Anda menekan ENTERtombol nextInt();tidak menangkap baris baru dan karenanya, melewatkan Scannerkode nanti.

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

sc.nextLine()lebih baik dibandingkan dengan parsing input. Karena kinerja bijaksana itu akan bagus.

shankar upadhyay
sumber
5

Saya kira saya sangat terlambat ke pesta ..

Seperti yang dinyatakan sebelumnya, menelepon input.nextLine()setelah mendapatkan nilai int Anda akan menyelesaikan masalah Anda. Alasan mengapa kode Anda tidak berfungsi adalah karena tidak ada lagi yang dapat disimpan dari input Anda (tempat Anda memasukkan int)string1 . Saya hanya akan memberi sedikit lebih banyak cahaya untuk seluruh topik.

Pertimbangkan nextLine () sebagai yang aneh di antara metode nextFoo () di kelas Scanner. Mari kita ambil contoh cepat .. Katakanlah kita memiliki dua baris kode seperti di bawah ini:

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

Jika kita memasukkan nilai di bawah ini (sebagai satu baris input)

54 234

Nilai variabel kami firstNumberdan secondNumbermenjadi 54 dan 234 masing-masing. Alasan mengapa ini bekerja seperti ini adalah karena umpan baris baru ( yaitu \ n ) TIDAK secara otomatis dihasilkan ketika metode nextInt () mengambil nilai-nilai. Ini hanya membutuhkan "int berikutnya" dan bergerak. Ini sama untuk sisa metode nextFoo () kecuali nextLine ().

nextLine () menghasilkan umpan baris baru segera setelah mengambil nilai; inilah yang dimaksud @RohitJain dengan mengatakan bahwa umpan baris baru "dikonsumsi".

Terakhir, metode next () hanya mengambil String terdekat tanpa menghasilkan baris baru; ini menjadikan ini metode preferensial untuk mengambil String terpisah dalam satu baris yang sama.

Saya harap ini membantu .. Selamat coding!

Taslim Oseni
sumber
apa itu "umpan baris baru"?
Pemanah
@Abcd Umpan baris baru pada dasarnya berarti 'mulai dari baris baru'.
Taslim Oseni
1
Contoh "54 234" itu benar-benar menjelaskan berbagai hal.
Alonso del Arte
3

Gunakan 2 objek pemindai bukan satu

Scanner input = new Scanner(System.in);
System.out.println("Enter numerical value");    
int option;
Scanner input2 = new Scanner(System.in);
option = input2.nextInt();
Harsh Shah
sumber
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
sumber
Jika Anda menggunakan metode nextLine () segera mengikuti metode nextInt (), ingat bahwa nextInt () membaca token integer; karena ini, karakter baris baru terakhir untuk jalur input integer masih dalam antrian di buffer input dan nextLine berikutnya () akan membaca sisa dari garis integer (yang kosong).
Neeraj Gahlawat
2

jika saya mengharapkan input yang tidak kosong

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


digunakan dalam contoh di atas:

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
sumber
2

Di salah satu usecase saya, saya memiliki skenario membaca nilai string yang didahului oleh beberapa nilai integer . Saya harus menggunakan loop " for / while " untuk membaca nilai-nilai. Dan tidak ada saran di atas yang berfungsi dalam kasus ini.

Menggunakan input.next()alih-alih input.nextLine()memperbaiki masalah. Semoga ini bisa bermanfaat bagi mereka yang berurusan dengan skenario serupa.

Giri
sumber
2

Gunakan kode ini untuk memperbaiki masalah Anda.

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
sumber
2

Karena nextXXX()metode tidak membaca newline, kecuali nextLine(). Kita dapat melewatkan newlinesetelah membaca non-stringnilai apa pun ( intdalam hal ini) dengan menggunakan scanner.skip()seperti di bawah ini:

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
sumber
0

Mengapa tidak menggunakan Scanner baru untuk setiap bacaan? Seperti di bawah ini. Dengan pendekatan ini Anda tidak akan menghadapi masalah Anda.

int i = new Scanner(System.in).nextInt();
Tobias Johansson
sumber
3
Tetapi kemudian Anda harus menutup Scanneruntuk mencegah kebocoran memori. Membuang-buang waktu?
TheCoffeeCup
1
Bukankah GC menangani hal itu jika Pemindai dibungkus dengan suatu metode? Seperti: String getInput () {return Scanner baru (System.in) .nextLine ()};
Tobias Johansson
Ini benar-benar salah. nextInt()tidak akan mengkonsumsi baris baru, terlepas dari apakah itu dalam "baru" Scanneratau yang sudah digunakan.
Dawood ibn Kareem