Scanner пропускает nextLine() после nextInt() или nextDouble(): как исправить?
Причина проблемы
Проблема связана с тем, как методы nextInt(), nextDouble() и другие nextFoo() обрабатывают ввод. Они считывают только само значение (число, слово и т.д.), но не потребляют символ новой строки (\n), который остаётся во входном буфере после нажатия Enter. Когда после этого вызывается nextLine(), он сразу же считывает этот оставшийся символ перевода строки и возвращает пустую строку.
Демонстрация проблемы
// Ввод: 25[Enter]
// Буфер после nextInt(): "\n"
// nextLine() считывает "\n" → возвращает ""
Способы решения
1. Добавить пустой nextLine() после nextInt()
Самый простой способ — явно "проглотить" оставшийся перевод строки:
Scanner scanner = new Scanner(System.in);
System.out.print("Введите возраст: ");
int age = scanner.nextInt();
scanner.nextLine(); // потребляем лишний перевод строки
System.out.print("Введите имя: ");
String name = scanner.nextLine();
System.out.println("Возраст: " + age + ", Имя: " + name);
2. Использовать nextLine() для всего, затем парсить
Более надёжный подход — всегда читать строки целиком и преобразовывать их в нужные типы:
Scanner scanner = new Scanner(System.in);
System.out.print("Введите возраст: ");
int age = Integer.parseInt(scanner.nextLine());
System.out.print("Введите имя: ");
String name = scanner.nextLine();
System.out.println("Возраст: " + age + ", Имя: " + name);
Преимущество: вы сами контролируете обработку ошибок. Недостаток: нужно обрабатывать исключения при парсинге.
3. Использовать Scanner.useDelimiter()
Можно изменить разделитель, чтобы nextInt() тоже потреблял перевод строки:
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter("\n"); // разделитель — только перевод строки
System.out.print("Введите возраст: ");
int age = scanner.nextInt(); // теперь nextInt() прочитает до \n и поглотит его
System.out.print("Введите имя: ");
String name = scanner.nextLine();
System.out.println("Возраст: " + age + ", Имя: " + name);
Какой способ выбрать?
- Для простых программ и учебных примеров — используйте способ 1 (пустой nextLine()). Это быстро и понятно.
- Для продакшн-кода — используйте способ 2 (nextLine() + парсинг). Это даёт полный контроль над вводом и упрощает обработку ошибок.
- Способ 3 — редко используется из-за побочных эффектов (например, проблемы с пробелами в строках).
Важное замечание
Проблема актуальна только для методов next(), nextInt(), nextDouble(), nextLong() и т.д. nextLine() сам по себе всегда потребляет перевод строки. Если вы используете только nextLine(), проблемы не возникнет.
Надеюсь, теперь стало понятно. Удачи в изучении Java!
<<Альтернативное решение: обёртка над Scanner
Можно создать собственный класс-обёртку, который автоматически решает эту проблему. Это полезно, если вы часто работаете с вводом в больших проектах.
import java.util.Scanner;
public class SafeScanner {
private Scanner scanner;
public SafeScanner() {
this.scanner = new Scanner(System.in);
}
public int nextInt() {
int value = scanner.nextInt();
scanner.nextLine(); // автоматически съедаем перевод строки
return value;
}
public String nextLine() {
return scanner.nextLine();
}
public double nextDouble() {
double value = scanner.nextDouble();
scanner.nextLine();
return value;
}
public void close() {
scanner.close();
}
}
// Использование:
public class Main {
public static void main(String[] args) {
SafeScanner scanner = new SafeScanner();
System.out.print("Введите возраст: ");
int age = scanner.nextInt(); // теперь не нужно писать лишний nextLine()
System.out.print("Введите имя: ");
String name = scanner.nextLine();
System.out.println("Возраст: " + age + ", Имя: " + name);
scanner.close();
}
}
Этот подход инкапсулирует "грязную" работу внутри обёртки, и ваш основной код становится чище. Недостаток — нужно поддерживать дополнительный класс, но для частого использования это оправдано.