9

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

14

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

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

System.out.println("Введите числовое значение");    
int option;
option = input.nextInt(); // Читаем числовое значение из ввода
System.out.println("Введите 1-ю строку"); 
String string1 = input.nextLine(); // Чтение 1-й строки (это пропускается)
System.out.println("Введите 2-ю строку");
String string2 = input.nextLine(); // Чтение 2-й строки (это появляется сразу после чтения числового значения)

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

Введите числовое значение
3   // Это мой ввод
Введите 1-ю строку    // Программа должна остановиться здесь и ждать мой ввод, но это пропускается
Введите 2-ю строку    // ...и эта строка выполняется и ждет мой ввод

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

5 ответ(ов)

2

Проблема заключается в методе input.nextInt(), который считывает только целочисленное значение. Когда вы продолжаете считывание с помощью input.nextLine(), вы получаете символ новой строки "\n", который возникает после нажатия клавиши Enter. Чтобы избежать этого, вам нужно добавить строку input.nextLine().

Попробуйте сделать это следующим образом:

System.out.print("Введите число: ");
int number = input.nextInt();
input.nextLine(); // Эта строка необходима (она считывает символ \n)
System.out.print("Текст1: ");
String text1 = input.nextLine();
System.out.print("Текст2: ");
String text2 = input.nextLine();

Таким образом, вы сначала считываете целое число, а затем очищаете буфер, чтобы подготовить его к следующему вводимому тексту.

0

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

Чтобы избежать этой проблемы, вы можете сразу после input.nextInt() использовать input.nextLine(). Это позволит "проглотить" символ конца строки и подготовить буфер для последующего ввода. Например:

int number = input.nextInt();
input.nextLine(); // Проглатываем символ новой строки
String text = input.nextLine(); // Теперь можно безопасно считать строку
0

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

ИЗМЕНЕНИЕ: как отметил @PatrickParker ниже, это может привести к бесконечному циклу, если пользователь введет любой пробел после числа. Ознакомьтесь с их ответом для лучшего паттерна, который можно использовать с skip: https://stackoverflow.com/a/42471816/143585.

0

TL;DR

Метод nextLine() безопасен для вызова, если (a) это первая инструкция чтения, (b) предыдущая инструкция также была nextLine().

Если вы не уверены, что хотя бы одно из вышеуказанного верно, вы можете использовать scanner.skip("\\R?") перед вызовом scanner.nextLine(), так как такие вызовы, как next(), nextInt(), оставляют потенциальный разделитель строки, созданный нажатием клавиши Enter, что влияет на результат nextLine(). Метод .skip("\\R?") позволит нам проигнорировать этот лишний разделитель строки.

Метод skip использует регулярные выражения, где

  • \R представляет разделители строк,
  • ? делает \R необязательным, что предотвратит выполнение метода skip в случаях:
    • ожидания совпадающей последовательности,
      • если конец все еще открытого источника данных, например, System.in, входного потока из сокета и т. д. будет достигнут,
    • выбрасывания java.util.NoSuchElementException, если
      • источник данных завершен/закрыт,
      • или если существующие данные не совпадают с тем, что мы хотим пропустить.

Важные моменты:

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

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

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

Проблема

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

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

Кстати, nextLine также потребляет эти разделители строк.

Решение

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

  • потребить разделитель строки, оставленный nextInt в кэше Scanner, вызывая
    • nextLine,
    • или, по моему мнению, более читаемым способом будет вызов skip("\\R") или skip("\r\n|\r|\n"), чтобы позволить Scanner пропустить часть, соответствующую разделителю строки (более подробная информация о \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.

0

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

В качестве альтернативы, вы можете сделать это в стиле C# и преобразовать строку в целое число следующим образом:

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

Этот способ также работает отлично и позволяет сэкономить строку кода.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь