Tarayıcı next () veya nextFoo () kullandıktan sonra nextLine () atlıyor mu?


684

Ben Scanneryöntemleri kullanıyorum nextInt()ve nextLine()girdi okumak için.

Şöyle görünüyor:

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)

Sorun, sayısal değeri girdikten sonra, ilkinin input.nextLine()atlanması ve ikincisinin input.nextLine()yürütülmesi, böylece çıktımın şöyle görünmesidir:

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

Uygulamamı test ettim ve sorun kullanımda yatıyor gibi görünüyor input.nextInt(). Bunu silerseniz, hem string1 = input.nextLine()ve string2 = input.nextLine()onları olmak istiyorum olarak yürütülür.


5
Ya da benim gibi olabilir ve BufferedReader'ı kullanabilirsiniz :) Eski okul olup olmadığı umurumda değil, her zaman işe yaradı ve her zaman benim için çalışacak. Ayrıca, BufferedReader bilgisinin başka yerlerde de uygulaması vardır. Tarayıcı'yı sevmiyorum.
Cruncher

Yanıtlar:


831

Bunun nedeni, Scanner.nextIntyöntemin girişinizde "Enter" tuşuna basarak oluşturulan yeni satır karakterini okumamasıdır ve dolayısıyla Scanner.nextLinebu satırsonu okunduktan sonra döndürülecek çağrı .

Scanner.nextLineSonra Scanner.next()veya herhangi bir Scanner.nextFooyöntemi ( nextLinekendisi hariç ) kullandığınızda benzer davranışla karşılaşacaksınız .

Çözüm:

  • Her Scanner.nextLinebirinden sonra bir çağrı yapın Scanner.nextIntveya yeniScanner.nextFoo hat dahil bu hattın geri kalanını tüketin

    int option = input.nextInt();
    input.nextLine();  // Consume newline left-over
    String str1 = input.nextLine();
    
  • Ya da daha iyisi, girdiyi okuyun Scanner.nextLineve girdinizi ihtiyacınız olan biçime dönüştürün. Örneğin, Integer.parseInt(String)yöntemi kullanarak bir tamsayıya dönüştürebilirsiniz .

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

4
@blekione. Kullanmalısınız try-catch, çünkü geçersiz bir argüman iletildiğinde Integer.parseIntatar NumberFormatException. İstisna hakkında daha sonra öğreneceksiniz. EG için: - Integer.parseInt("abc"). "Abc" nin int'e dönüştürülmesini istemiyor musunuz?
Rohit Jain

3
@blekione. Yani, yukarıdaki durumda, kodunuz bu noktada durur ve yürütmeye devam edemezsiniz. İstisna İşleme ile bu tür durumlarla başa çıkabilirsiniz.
Rohit Jain

4
İkincisi ne ölçüde daha iyidir? AFAIK Tarayıcı # nextInt (), grup virgüllerine ve yerel ayar öneklerine ve soneklerine izin vererek doğru verileri bulmakta çok daha yumuşaktır. Tamsayı # parseInt () yalnızca rakamlara ve ondalık basamağa ek olarak isteğe bağlı bir işarete izin verir.
Mordechai

5
Şahsen Scanner#hasNextFoobir try-catch yerine önceden bir çek tercih ederim , ama bu da işe yarıyor.
Milquetoast

1
ParseDouble kullan yeni bir sorun var, nextDouble ondalık (. Veya,) bölgesel yapılandırmasını kullanır, ancak Parsedouble her zaman ABD ondalık (.) Alır.
olmerg

209

Sorun input.nextInt () yöntemiyle ilgilidir - sadece int değerini okur. Girdi.nextLine () ile okumaya devam ettiğinizde "\ n" Enter tuşunu alırsınız. Bunu atlamak için input.nextLine () öğesini eklemeniz gerekir . Umarım bu şimdi açık olmalıdır.

Şöyle deneyin:

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, atlanan satırı kullanılmaz hale getiren bir çözüm gibi görünüyor nextLine, ancak yine de bu davranışın bir açıklamasına ihtiyacım var
Eng.Fouad


Soru: 27 Ekim 12'de 16: 37'de sordu. 14 Ağustos 11, 12:24 yanıtladı. Bu nasıl mümkün olabilir?
Apoorv Patne

79

Ardından da düğmesine bir sayı girdiğinizde da ondan Enter, input.nextInt()sadece numarayı değil, "hat sonu" tüketir. Zaman input.nextLine()yürütür, ilk girişten tampon içinde, hala "hat sonu" tüketir.

Bunun yerine, input.nextLine()hemeninput.nextInt()


7
@Victor hata değil. belirtildiği gibi çalışıyor. yine de, api'ye hedef girdiyi izleyen herhangi bir boşluğu tüketmesini söylemenin kolay bir yolu olması gerektiğini savunabilirsiniz.
Bohemian

Anlıyorum. teşekkürler @Bohemian, tam olarak ne tartışıyorum. Bunun değiştirilmesi gerektiğini düşünüyorum, belki de bana bir sonraki JCP'de bu "meseleyi" nereye önereceğimi gösterebilirsin.
Victor

@victor Hata Bildir veya Özellik İste sayfasından bir özellik isteyebilir (ve kodun nasıl katkıda bulunacağını öğrenebilirsiniz) .
Bohemian


48

Bu konuyla ilgili birçok soru var gibi görünüyor java.util.Scanner. Bence daha okunabilir / deyimsel bir çözüm scanner.skip("[\r\n]+")aradıktan sonra herhangi bir satırsonu karakteri bırakmak için aramak olacaktır nextInt().

EDIT: @PatrickParker aşağıda belirtildiği gibi, kullanıcı sayıdan sonra herhangi bir boşluk girerse, bu sonsuz bir döngü neden olur. Atla ile kullanmak için daha iyi bir model için yanıtlarına bakın: https://stackoverflow.com/a/42471816/143585



Arabelleğe veri kaldırmak için ne yaptığımızı biliyorum, ama bu durumda, lütfen bana bu konuda yardım edin: stackoverflow.com/questions/33585314/having-issues-with-scanner
Khuong

1
FYI, bu sonsuz bir döngü neden kullanıcı sayısal girişten sonra herhangi bir sekme veya boşluk türleri olmasıdır. Daha iyi bir atlama için cevabımı burada görebilirsiniz: stackoverflow.com/a/42471816/7098259
Patrick Parker

@PatrickParker: gerçekten sonsuz bir döngüye neden oluyor! Teşekkürler, cevabını sizinkine bağlanmak için düzenledi.
Denis Tulskiy

33

Bunu yapar çünkü input.nextInt();satırsonu yakalanmaz. input.nextLine();altına ekleyerek diğerlerini beğenebilirsin .
Alternatif olarak C # stilini yapabilir ve bir nextLine öğesini aşağıdaki gibi bir tam sayıya ayrıştırabilirsiniz:

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

Bunu yapmak da işe yarıyor ve size bir kod satırı kazandırıyor.



1
Burada try catch kullanmak zorunda. Giriş bir sayı değilse ne olur. NumberFormatException burada ele alınmalıdır.
Arundev

Bu, bunu denediğiniz her satırda yalnızca bir int jetonu olduğunu varsayar.
cellepo

25

TL; DR scanner.skip("\\R")Her scanner.newLine()çağrıdan önce kullanın , sonra:

  • scanner.next()
  • scanner.next*TYPE*() yöntem.

Bilmeniz gerekenler:

  • Birkaç satırı temsil eden metin, satırlar arasında yazdırılamayan karakterler de içerir (bunlara satır ayırıcılar diyoruz)

    • satır başı (CR - inç olarak gösterilen Dize değişmez değerleri "\r")
    • satır besleme (LF - olarak gösterilen Dize değişmez değerleri "\n")
  • Konsoldan veri okurken, kullanıcının yanıtını yazmasına izin verir ve bittiğinde bir şekilde bu gerçeği doğrulaması gerekir . Bunun için kullanıcının klavyede "enter" / "return" tuşuna basması gerekir.

    Önemli olan, kullanıcı verilerinin standart girdiye ( System.inokunmasıyla temsil edilir) yerleştirilmesinin yanında, bu anahtarın Scannerayrıca işletim sistemine bağlı satır ayırıcıları (Windows için olduğu gibi) göndermesidir \r\n.

    Bu nedenle, kullanıcıdan değer istendiğinde ageve kullanıcı türü 42 ve enter tuşuna bastığınızda , standart girdi içerecektir "42\r\n".

Sorun

Scanner#nextInt(ve diğer yöntemler) Tarayıcı izin vermez tüketmek bu satırları ayırmak. Bunları okuyacaktır (Tarayıcı, boşluktan bakacak değerden daha fazla rakam gösteren kullanıcıdan başka basamak olmadığını nasıl bilebilir ?) Ve bunları standart girdiden kaldıracak, ancak bu satır ayırıcılarını dahili olarak önbelleğe alacaktır . Hatırlamamız gereken şey, tüm Tarayıcı yöntemlerinin önbelleğe alınan metinden başlayarak her zaman taramasıdır. Scanner#nextTypeSystem.inage

Artık satır ayırıcılar (veya akış sonu) bulana kadarScanner#nextLine() tüm karakterleri toplar ve döndürür . Ancak, konsoldan numarayı okuduktan sonra satır ayırıcılar hemen Scanner'ın önbelleğinde bulunduğundan, boş String döndürür, yani Tarayıcı bu satır ayırıcılarından (veya akış sonundan) önce herhangi bir karakter bulamadı. BTW ayrıca bu hat ayırıcılarını da tüketir .
nextLine

Çözüm

Bu nedenle, sonuç olarak boş dizeden kaçınırken sayı ve ardından tüm satır için sormak istediğinizde nextLine,

  • nextIntTarayıcı önbelleğinden kalan satır ayırıcısını tüket
    • arıyor nextLine,
    • veya IMO'nun daha okunabilir bir yolu, arayarak skip("\\R")veya skip("\r\n|\r|\n")Tarayıcının hat ayırıcıyla eşleşen parçayı atlamasına izin vermek olabilir (hakkında daha fazla bilgi \R: https://stackoverflow.com/a/31060125 )
  • hiç nextInt(ya nextda herhangi bir nextTYPEyöntem) kullanmayın . Bunun yerine, tüm veri hattını-satır kullanılarak okundu nextLineHer bir çizgiden ve ayrıştırma numaraları gibi uygun tip (bir satır varsayarak tek sayı içerir) intile Integer.parseInt.

BTW : yöntemler, tarayıcı tarafından önbelleğe alınanlar da dahil olmak üzere, bir sonraki sınırlayıcı olmayan değeri (jeton) bulana kadar sınırlayıcıları (varsayılan olarak sekmeler, satır ayırıcılar gibi tüm boşluklar) atlayabilir . Kod benzeri girişler için teşekkürlerScanner#nextType"42\r\n\r\n321\r\n\r\n\r\nfoobar"

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

uygun şekilde atayabilecektir num1=42 num2=321 name=foobar.


14

Yerine input.nextLine()kullanımı input.next(), bu sorunu çözmek gerekir.

Değiştirilmiş 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();
}


13

Hem dizeleri hem de girişleri okumak istiyorsanız, bir çözüm iki Tarayıcı kullanmaktır:

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
Bir süredir aradığım şey buydu. Akıllı hareket!
Liam Wilson

1
@BdLearner scanner.nextLine()çalışmalı.
André Valenti

11

Tarayıcı sınıfı nextLine () yöntemine karışmadan girişi hızlı bir şekilde taramak istiyorsanız, bunun için Özel Giriş Tarayıcısını kullanın.

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

}

Avantajları:

  • Girişi BufferReader'dan daha hızlı tarar
  • Zaman Karmaşıklığını Azaltır
  • Sonraki her giriş için Tamponu temizler

Yöntemler:

  • scanChar () - tek karakteri tara
  • scanInt () - Tamsayı değerini tara
  • scanLong () - Uzun değeri tara
  • scanString () - Dize değerini tara
  • scanDouble () - Çift değeri tara
  • scanInt (int [] dizisi) - tam Diziyi (Tamsayı) tarar
  • scanLong (long [] dizisi) - tam Diziyi tarar (Long)

Kullanımı:

  1. Java kodunuzun altındaki Verilen Kodu kopyalayın.
  2. Verilen Sınıf için Nesneyi Başlatma

ScanReader sc = new ScanReader(System.in); 3. Gerekli Sınıfları İçe Aktarın:

import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; 4. İstisna'yı işlemek için ana metodunuzdan IOException istisnasını kullanın. 5. Sağlanan Yöntemleri Kullanın. 6. Keyfini Çıkarın

Misal :

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

Sorunu önlemek için , arabellek temizlenmesine yardımcı olduğundan nextLine();hemen sonra kullanın nextInt();. Tuşuna bastığınızda yeni bir satır yakalamak ve dolayısıyla atlıyor sonra kodu.ENTERnextInt();Scanner

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

9

sc.nextLine()girdiyi ayrıştırmaya kıyasla daha iyidir. Çünkü performans akıllıca olacaktır.


5

Sanırım partiye oldukça geç kaldım ..

Daha önce belirtildiği gibi, input.nextLine()int değerinizi aldıktan sonra çağırmak sorununuzu çözecektir. Kodunuzun çalışmamasının nedeni, girişinizden (int'i girdiğiniz yerde) saklanacak başka bir şey olmamasıydı string1. Tüm konuya biraz daha ışık tutacağım.

Düşünün () nextLine arasında tek bir takım olarak nextFoo () Tarayıcı sınıfındaki yöntemlerden. Kısa bir örnek verelim .. Diyelim ki aşağıdaki gibi iki satır kodumuz var:

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

Aşağıdaki değeri girersek (tek bir giriş satırı olarak)

54 234

Bizim firstNumberve secondNumberdeğişkenimizin değeri sırasıyla 54 ve 234 olur. Yeni bir satır besleme (çünkü bunun bu şekilde çalışır nedeni budur yani \ n ) DEĞİLDİR nextInt () yöntemi değerlerinde aldığında otomatik olarak oluşturulur. Sadece "sonraki int" alır ve devam eder. NextLine () hariç nextFoo () yöntemlerinin geri kalanı için aynıdır.

nextLine (), bir değer aldıktan hemen sonra yeni bir satır feed'i oluşturur; @RohitJain yeni satır özet akışının "tüketildiğini" söyleyerek bunu ifade eder.

Son olarak, bir sonraki () yöntemi yakın dize alır olmayan yeni bir satır üretilmesi; Bu, bunu aynı tek satırda ayrı Dizeler almak için tercih edilen yöntem yapar.

Umarım bu yardımcı olur .. Mutlu kodlama!


"yeni satır feed'i" nedir?
Okçu

@Abcd Yeni bir satır besleme temelde 'yeni bir satırdan başlamak' anlamına gelir.
Taslim Oseni

1
Bu "54 234" örneği gerçekten işleri açıklığa kavuşturuyor.
Alonso del Arte

3

Bir yerine 2 tarayıcı nesnesi kullanın

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

NextInt () yönteminin hemen ardından nextLine () yöntemini kullanırsanız, nextInt () öğesinin tamsayı belirteçlerini okuduğunu hatırlayın; Bu nedenle, bu tamsayı girişi satırı için son satır karakteri hala giriş arabelleğinde sıraya alınır ve sonraki nextLine (), tam sayı satırının (boş olan) geri kalanını okuyacaktır.
Neeraj Gahlawat

2

boş olmayan bir girdi bekliyorsam

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


yukarıdaki örnekte kullanılan:

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

Benim usecase birinde, önünde bir tamsayı değer birkaç dize değeri okuma senaryosu vardı . Değerleri okumak için bir " for / while döngüsü " kullanmak zorunda kaldı . Ve yukarıdaki önerilerin hiçbiri bu durumda işe yaramadı.

Kullanma input.next()yerine input.nextLine()sabit sayısında. Umarım bu benzer senaryo ile ilgilenenler için yararlı olabilir.


2

Bu kodu kullanın sorununuzu çözecektir.

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

Gibi nextXXX()yöntemler okumaz newline, hariç nextLine(). Aşağıdaki gibi kullanarak newlineherhangi bir non-stringdeğeri ( intbu durumda) okuduktan sonra atlayabiliriz scanner.skip():

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

Neden her okuma için yeni bir Tarayıcı kullanmıyorsunuz? Aşağıdaki gibi. Bu yaklaşımla probleminizle yüzleşmeyeceksiniz.

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

3
Ama sonra Scannerbellek sızıntısını önlemek için. Zamanı boşa harcamak?
TheCoffeeCup

1
Tarayıcı bir yönteme sarılmışsa GC bununla ilgilenmez mi? Like: String getInput () {return new Scanner (System.in) .nextLine ()};
Tobias Johansson

Bu kesinlikle yanlış. nextInt()"yeni" Scannerya da zaten kullanılmış olsun, yeni satırı kullanmaz.
Dawood ibn Kareem
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.