Сканер пропускает nextLine () после использования next () или nextFoo ()?

684

Я использую Scannerметоды nextInt()и nextLine()для чтения ввода.

Это выглядит так:

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)

Проблема в том, что после ввода числового значения первое input.nextLine()пропускается, а второе input.nextLine()выполняется, так что мой вывод выглядит так:

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

Я протестировал свое приложение, и похоже, что проблема заключается в использовании input.nextInt(). Если я его удаляю, то оба string1 = input.nextLine()и string2 = input.nextLine()выполняются так, как я хочу, чтобы они были.

blekione
источник
5
Или вы можете быть как я и использовать BufferedReader :) Мне все равно, если это старая школа, она всегда работала и всегда будет работать для меня. Кроме того, знание BufferedReader имеет применение в других местах. Мне просто не нравится сканер.
Cruncher

Ответы:

831

Это потому, что Scanner.nextIntметод не читает символ новой строки в вводе, созданном нажатием «Enter», и поэтому вызов Scanner.nextLinereturn возвращается после чтения этой новой строки .

С подобным поведением вы столкнетесь, когда будете использовать Scanner.nextLineafter Scanner.next()или любой другой Scanner.nextFooметод (кроме nextLineсамого себя).

Временное решение:

  • Либо ставить Scanner.nextLineвызов после каждого, Scanner.nextIntлибо Scanner.nextFooиспользовать остаток этой строки, включая перевод строки

    int option = input.nextInt();
    input.nextLine();  // Consume newline left-over
    String str1 = input.nextLine();
  • Или, что еще лучше, прочитайте ввод Scanner.nextLineи преобразуйте его в нужный вам формат. Например, вы можете преобразовать в целое число, используя Integer.parseInt(String)метод.

    int option = 0;
    try {
        option = Integer.parseInt(input.nextLine());
    } catch (NumberFormatException e) {
        e.printStackTrace();
    }
    String str1 = input.nextLine();
Рохит Джайн
источник
4
@blekione. Вы должны использовать try-catch, потому что Integer.parseIntбросает, NumberFormatExceptionкогда ему передается неверный аргумент. Вы узнаете об исключении позже. Для EG: - Integer.parseInt("abc"). Вы не хотите, чтобы "abc" конвертировался в int, верно?
Рохит Джейн
3
@blekione. Таким образом, в приведенном выше случае ваш код остановится в этот момент, и вы не сможете продолжить выполнение. С обработкой исключений вы можете справиться с такими условиями.
Рохит Джейн
4
Насколько последний лучше? AFAIK Scanner # nextInt () гораздо более мягок в поиске правильных целых чисел, позволяя групповые запятые и региональные префиксы и суффиксы. Integer # parseInt () допускает только цифры и десятичную точку плюс необязательный знак.
Мордехай
5
Лично я предпочитаю Scanner#hasNextFooчек заранее, а не попытку, но это тоже работает.
MildlyMilquetoast
1
Использование ParseDouble имеет новую проблему: в nextDouble используется региональная конфигурация десятичного числа (. Или,), но Parsedouble всегда получает десятичное число в США (.).
olmerg
209

Проблема в методе input.nextInt () - он только читает значение int. Поэтому, когда вы продолжаете чтение с помощью input.nextLine (), вы получаете клавишу ввода «\ n». Поэтому, чтобы пропустить это, вы должны добавить input.nextLine () . Надеюсь, это должно быть ясно сейчас.

Попробуйте это так:

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();
Прайн
источник
ммм кажется решением, делающим пропущенную строку неиспользуемой nextLine, но мне все еще нужно объяснение этого поведения
Eng.Fouad
2
FYI: слился с stackoverflow.com/questions/7056749/...
Shog9
Вопрос: спросил 27 октября 12 года в 16:37. ответил 14 августа 11 в 12:24. Как это вообще возможно?
Апур Патн
79

Это потому, что когда вы вводите число, а затем нажимаете Enter, input.nextInt()потребляет только номер, а не «конец строки». При выполнении input.nextLine()он потребляет «конец строки», все еще находящийся в буфере с первого ввода.

Вместо этого используйте input.nextLine()сразу послеinput.nextInt()

Богемский
источник
7
@Victor это не ошибка. это работает как указано. Вы можете поспорить, однако, что должен быть простой способ указать API, чтобы он также занимал любые пробелы после целевого ввода.
Богемный
Понимаю. спасибо @ Bohemian, это именно то, о чем я спорю. Я думаю, что это должно быть изменено, возможно, вы можете указать мне, где предложить этот «вопрос» в следующем JCP.
Виктор
@victor Вы можете запросить (и узнать, как внести код в) функцию через страницу « Сообщить об ошибке» или «Запросить функцию» .
Богемский
FYI: слился с stackoverflow.com/questions/7056749/...
Shog9
48

Кажется, есть много вопросов по этому вопросу java.util.Scanner. Я думаю, что более читаемым / идиоматическим решением было бы позвонить, scanner.skip("[\r\n]+")чтобы удалить любые символы новой строки после вызова nextInt().

РЕДАКТИРОВАТЬ: как @PatrickParker отметил ниже, это приведет к бесконечному циклу, если пользователь вводит любой пробел после числа. Смотрите их ответ для лучшего шаблона для использования с пропустить: https://stackoverflow.com/a/42471816/143585

Денис Тульский
источник
FYI: слился с stackoverflow.com/questions/7056749/...
Shog9
Я знаю, что мы делаем, чтобы удалить данные из буфера, но в этом случае, пожалуйста, помогите мне с этим: stackoverflow.com/questions/33585314/having-issues-with-scanner
Khuong
1
К вашему сведению, это приведет к бесконечному циклу, если пользователь вводит любые символы табуляции или пробелы после числового ввода. Смотрите мой ответ здесь для лучшего пропуска: stackoverflow.com/a/42471816/7098259
Патрик Паркер
@PatrickParker: это действительно вызывает бесконечный цикл! Спасибо, отредактировал ответ по ссылке на свой.
Денис Тульский
33

Это происходит потому, input.nextInt();что не захватывает перевод строки. Вы можете сделать как другие предложенные, добавив input.nextLine();внизу.
В качестве альтернативы вы можете сделать это в стиле C # и проанализировать nextLine в целое число следующим образом:

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

Делать это работает так же хорошо, и это экономит вам строку кода.

Электрический кофе
источник
FYI: слился с stackoverflow.com/questions/7056749/...
Shog9
1
Вы должны использовать попробовать поймать здесь. Что будет, если на входе не число. NumberFormatException должны быть обработаны здесь.
Арундев
Это предполагает, что в каждой строке есть только один токен int, для которого вы пытаетесь это сделать.
cellepo
25

TL; DR Использовать scanner.skip("\\R")перед каждым scanner.newLine()вызовом, который выполняется после:

  • scanner.next()
  • scanner.next*TYPE*() метод.

Вещи, которые вам нужно знать:

  • текст, представляющий несколько строк, также содержит непечатаемые символы между строками (мы называем их разделителями строк), например

    • возврат каретки (CR - в строковых литералах, представленных как "\r")
    • перевод строки (LF - в строковых литералах, представленных как "\n")
  • когда вы читаете данные из консоли, это позволяет пользователю вводить свой ответ, а когда он это делает, ему необходимо каким-то образом подтвердить этот факт. Для этого пользователю необходимо нажать клавишу «ввод» / «возврат» на клавиатуре.

    Важно то, что этот ключ помимо обеспечения помещения пользовательских данных на стандартный ввод (представленный, System.inкоторый читается Scanner) также отправляет зависимые от ОС разделители строк (как для Windows \r\n) после него.

    Поэтому, когда вы запрашиваете у пользователя значение типа age, а пользователь вводит 42 и нажимает ввод, стандартный ввод будет содержать "42\r\n".

проблема

Scanner#nextInt(и другие методы) не позволяют СканеруScanner#nextType использовать эти разделители строк. Он будет читать их из System.in(как иначе Сканер узнает, что от пользователя больше нет цифр, представляющих ageзначение, а не с пробелами?), Что удалит их из стандартного ввода, но он также кеширует эти разделители строк внутри . Нам нужно помнить, что все методы Scanner всегда сканируют, начиная с кэшированного текста.

Сейчас же Scanner#nextLine() просто собирает и возвращает все символы, пока не найдет разделители строк (или конец потока). Но поскольку разделители строк после считывания числа с консоли сразу обнаруживаются в кэше сканера, он возвращает пустую строку, то есть сканер не смог найти ни одного символа перед этими разделителями строк (или концом потока).
КСТАТИ nextLineтакже потребляет эти разделители строк.

Решение

Поэтому, когда вы хотите запросить номер, а затем и всю строку, избегая при этом пустую строку nextLine, либо

  • использовать разделитель строк, оставленный nextIntиз кэша сканеров
    • звонит nextLine,
    • или IMO более читабельным способом было бы позвонить skip("\\R")или skip("\r\n|\r|\n")позволить сканеру пропустить часть, совпадающую с разделителем строк (подробнее о \R: https://stackoverflow.com/a/31060125 )
  • не используйте nextInt(ни nextкакие-либо nextTYPEметоды) вообще. Вместо этого читайте все данные построчно с помощью nextLineи анализируйте номера из каждой строки (при условии, что одна строка содержит только одно число) для правильного типа, например, intчерез Integer.parseInt.

КСТАТИ : методы могут пропустить разделители (по умолчанию всех непечатаемые как вкладки, разделители строк) , включая те , кэшируется сканером, пока они не найдут следующее значение без разделителей (маркер). Спасибо за ввод кодаScanner#nextType"42\r\n\r\n321\r\n\r\n\r\nfoobar"

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

сможет правильно назначить num1=42 num2=321 name=foobar.

Pshemo
источник
14

Вместо input.nextLine()использования input.next()это должно решить проблему.

Модифицированный код:

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
источник
FYI: слился с stackoverflow.com/questions/7056749/...
Shog9
13

Если вы хотите прочитать как строки, так и целые числа, решение состоит в том, чтобы использовать два сканера:

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
Это то, что я искал, в течение некоторого времени. Умный ход!
Лиам Уилсон,
1
@BdLearner scanner.nextLine()должен работать.
Андре Валенти
11

Если вы хотите быстро сканировать ввод, не путаясь с методом класса сканера nextLine (), используйте для этого пользовательский сканер ввода.

Код:

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

}

Преимущества:

  • Сканирует ввод быстрее, чем BufferReader
  • Уменьшает сложность времени
  • Сбрасывает буфер для каждого следующего ввода

Методы:

  • scanChar () - сканировать один символ
  • scanInt () - сканирует целочисленное значение
  • scanLong () - сканировать длинное значение
  • scanString () - сканирует строковое значение
  • scanDouble () - сканировать двойное значение
  • scanInt (int [] array) - сканирует полный массив (целое число)
  • scanLong (long [] array) - сканирует полный массив (Long)

Применение :

  1. Скопируйте данный код под свой код Java.
  2. Объект инициализации для данного класса

ScanReader sc = new ScanReader(System.in); 3. Импортируйте необходимые классы:

import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; 4. Бросьте IOException из вашего основного метода для обработки Exception 5. Используйте предоставленные методы. 6. Наслаждайтесь

Пример :

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

Чтобы избежать проблемы, используйте nextLine();сразу после, nextInt();поскольку это помогает в очистке буфера. При нажатии не захватывает новую строку и , следовательно, пропускает код позже.ENTERnextInt();Scanner

Scanner scanner =  new Scanner(System.in);
int option = scanner.nextInt();
scanner.nextLine(); //clearing the buffer
Урваши Гупта
источник
9

sc.nextLine()лучше по сравнению с анализом ввода. Потому что по производительности это будет хорошо.

Шанкар Упадхьяй
источник
5

Я думаю, что я довольно поздно на вечеринку ..

Как уже говорилось, вызов input.nextLine()после получения значения int решит вашу проблему. Причина, по которой ваш код не работал, заключалась в том, что больше нечего было хранить из вашего ввода (куда вы ввели int) string1. Я просто пролью немного света на всю тему.

Рассмотрим nextLine () как нечетный среди методов nextFoo () в классе Scanner. Давайте рассмотрим небольшой пример. Допустим, у нас есть две строки кода, подобные приведенным ниже:

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

Если мы введем значение ниже (в виде одной строки ввода)

54 234

Значения наших firstNumberи secondNumberпеременных становятся 54 и 234 соответственно. Причина, по которой это работает таким образом, заключается в том, что новый перевод строки ( т.е. \ n ) НЕ генерируется автоматически, когда метод nextInt () принимает значения. Это просто берет "следующий int" и движется дальше. То же самое относится и к остальным методам nextFoo (), за исключением nextLine ().

nextLine () генерирует новый перевод строки сразу после получения значения; это то, что @RohitJain означает, говоря, что новый перевод строки "потребляется".

Наконец, метод next () просто берет ближайшую строку без генерации новой строки; это делает этот метод предпочтительным для получения отдельных строк в одной строке.

Я надеюсь, что это помогает .. Веселое кодирование!

Таслим Осени
источник
Что такое "перевод строки"?
Арчер
@Abcd Новый перевод строки в основном означает «начиная с новой строки».
Таслит Осень
1
Этот пример «54 234» действительно проясняет ситуацию.
Алонсо дель Арте
3

Используйте 2 объекта сканера вместо одного

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);
    }
Нирадж Гахлават
источник
Если вы используете метод nextLine (), следующий сразу за методом nextInt (), вспомните, что nextInt () читает целочисленные токены; из-за этого последний символ новой строки для этой строки целочисленного ввода все еще находится в очереди во входном буфере, и следующая функция nextLine () будет считывать остаток от целочисленной строки (которая является пустой).
Нирадж Гахлават
2

если я ожидаю непустой ввод

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


используется в приведенном выше примере:

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

В одном из моих вариантов использования у меня был сценарий чтения строкового значения, которому предшествовала пара целочисленных значений . Я должен был использовать цикл for / while для чтения значений. И ни одно из вышеперечисленных предложений не сработало в этом случае.

Использование input.next()вместо input.nextLine()исправленной проблемы. Надеюсь, что это может быть полезно для тех, кто имеет дело с подобным сценарием.

Гири
источник
2

Используйте этот код, чтобы решить вашу проблему.

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

Как nextXXX()методы не читают newline, кроме nextLine(). Мы можем пропустить newlineпосле прочтения любого non-stringзначения ( intв этом случае), используя 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

Почему бы не использовать новый сканер для каждого чтения? Как ниже. При таком подходе вы не столкнетесь со своей проблемой.

int i = new Scanner(System.in).nextInt();
Тобиас Йоханссон
источник
3
Но тогда вы должны закрыть, Scannerчтобы предотвратить утечку памяти. Трата времени?
TheCoffeeCup
1
Разве GC не позаботится об этом, если сканер обернут в метод? Например: String getInput () {return new Scanner (System.in) .nextLine ()};
Тобиас Йоханссон
Это абсолютно неверно. nextInt()не будет использовать новую строку независимо от того, является ли она новой Scannerили уже использованной.
Дауд ибн Карим