¿El escáner se salta nextLine() después de usar next() o nextFoo()?
Estoy usando los Scanner
métodos nextInt()
y nextLine()
para leer entradas.
Se parece a esto:
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)
El problema es que luego de ingresar el valor numérico, input.nextLine()
se omite el primero y input.nextLine()
se ejecuta el segundo, por lo que mi salida queda así:
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
Probé mi aplicación y parece que el problema radica en el uso input.nextInt()
. Si lo elimino, ambos string1 = input.nextLine()
y string2 = input.nextLine()
se ejecutan como quiero.
Esto se debe a que el Scanner.nextInt
método no lee el carácter de nueva línea en la entrada creada al presionar "Enter", por lo que la llamada regresa Scanner.nextLine
después de leer esa nueva línea .
Encontrará un comportamiento similar cuando utilice Scanner.nextLine
after Scanner.next()
o cualquier Scanner.nextFoo
método (excepto nextLine
él mismo).
Solución alterna:
Realice una
Scanner.nextLine
llamada después de cada unaScanner.nextInt
oScanner.nextFoo
consuma el resto de esa línea, incluida la nueva línea.int option = input.nextInt(); input.nextLine(); // Consume newline left-over String str1 = input.nextLine();
O, mejor aún, lea la entrada
Scanner.nextLine
y conviértala al formato adecuado que necesite. Por ejemplo, puede convertir a un número entero usandoInteger.parseInt(String)
el método.int option = 0; try { option = Integer.parseInt(input.nextLine()); } catch (NumberFormatException e) { e.printStackTrace(); } String str1 = input.nextLine();
El problema está en el input.nextInt()
método; solo lee el valor int. Entonces, cuando continúe leyendo, input.nextLine()
recibirá la tecla Intro "\n". Entonces, para omitir esto, debes agregar el archivo input.nextLine()
.
Pruébelo así, en su lugar:
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();
Es porque cuando ingresa un número y luego presiona Enter, input.nextInt()
consume solo el número, no el "final de línea". Cuando input.nextLine()
se ejecuta, consume el "fin de línea" que aún está en el búfer desde la primera entrada.
En su lugar, úselo input.nextLine()
inmediatamente despuésinput.nextInt()
Parece haber muchas preguntas sobre este tema java.util.Scanner
. Creo que una solución más legible/idiomática sería llamar scanner.skip("[\r\n]+")
para eliminar cualquier carácter de nueva línea después de llamar nextInt()
.
EDITAR: como @PatrickParker señaló a continuación, esto provocará un bucle infinito si el usuario ingresa cualquier espacio en blanco después del número. Vea su respuesta para conocer un mejor patrón para usar con skip: https://stackoverflow.com/a/42471816/143585
TL;DR
nextLine()
es seguro llamar cuando (a) es la primera instrucción de lectura, (b) la instrucción de lectura anterior también fue nextLine()
.
Si no está seguro de que alguna de las opciones anteriores sea cierta, puede usarla scanner.skip("\\R?")
antes de llamar scanner.nextLine()
, ya que llamadas como next()
nextInt()
dejarán un posible separador de línea, creado con la tecla de retorno , lo que afectará el resultado de nextLine()
. Nos .skip("\\R?")
permitirá consumir este separador de línea innecesario.
skip
usa expresiones regulares donde
\R
representa separadores de línea?
lo hará\R
opcional, lo que evitaráskip
que el método:- esperando secuencia coincidente
- en caso de llegar al final de una fuente de datos aún abierta como
System.in
, flujo de entrada desde socket, etc.
- en caso de llegar al final de una fuente de datos aún abierta como
- tirar
java.util.NoSuchElementException
en caso de- fuente de datos cancelada/cerrada,
- o cuando los datos existentes no coinciden con lo que queremos omitir
- esperando secuencia coincidente
Cosas que necesitas saber:
El texto que representa pocas líneas también contiene caracteres no imprimibles entre líneas (los llamamos separadores de líneas) como
retorno de carro (CR - en literales de cadena representados como
"\r"
)avance de línea (LF - en literales de cadena representados como
"\n"
)cuando estás leyendo datos de la consola, le permite al usuario escribir su respuesta y cuando termina necesita confirmar de alguna manera ese hecho. Para hacerlo, el usuario debe presionar la tecla "enter"/"return" en el teclado.
Lo importante es que esta clave, además de garantizar la colocación de los datos del usuario en la entrada estándar (representada por System.in
la cual se lee Scanner
), también envía separadores de línea dependientes del sistema operativo (como para Windows \r\n
) después.
Entonces, cuando le pide al usuario un valor como age
y el usuario escribe 42 y presiona Enter, la entrada estándar contendrá "42\r\n"
.
Problema
Scanner#nextInt
(y otros métodos) no permite que Scanner consuma estos separadores de línea. Los leerá (¿de qué otra manera Scanner sabría que no hay más dígitos del usuario que representen valor que los espacios en blanco?), lo que los eliminará de la entrada estándar, pero también almacenará en caché esos separadores de línea internamente . Lo que debemos recordar es que todos los métodos del escáner siempre escanean a partir del texto almacenado en caché.Scanner#nextType
System.in
age
Ahora Scanner#nextLine()
simplemente recopila y devuelve todos los caracteres hasta que encuentre separadores de línea (o el final de la secuencia). Pero dado que los separadores de línea después de leer el número de la consola se encuentran inmediatamente en la memoria caché del Scanner, devuelve una Cadena vacía, lo que significa que Scanner no pudo encontrar ningún carácter antes de esos separadores de línea (o el final de la secuencia).
Por cierto, nextLine
también consume esos separadores de línea.
Solución
Entonces, cuando desee solicitar el número y luego la línea completa evitando esa cadena vacía como resultado de nextLine
, ya sea
- consumir el separador de línea dejado por
nextInt
la caché de los escáneres por - llamando
nextLine
, - O, en mi opinión , una forma más legible sería llamar
skip("\\R")
oskip("\r\n|\r|\n")
permitir que el escáner omita la parte que coincide con el separador de línea (más información sobre\R
: https://stackoverflow.com/a/31060125 ) - no utilice
nextInt
(ninext
, ni ningúnnextTYPE
método) en absoluto. En su lugar, lea los datos completos línea por línea usandonextLine
y analice los números de cada línea (asumiendo que una línea contiene solo un número) para escribirlos correctamente, comoint
viaInteger.parseInt
.
Por cierto : los métodos pueden omitir los delimitadores (de forma predeterminada, todos los espacios en blanco como tabulaciones, separadores de líneas), incluidos los almacenados en caché por el escáner, hasta que encuentren el siguiente valor no delimitador (token). Gracias a eso por ingresar como código.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();
podrá asignar correctamente num1=42
num2=321
name=foobar
.