TL; DR Uso scanner.skip("\\R")
antes de cada scanner.newLine()
llamada, que se ejecuta después de:
scanner.next()
scanner.next*TYPE*()
método.
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ínea) 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á leyendo datos desde 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 la System.in
cual se lee Scanner
), también envía separadores de línea dependientes del sistema operativo (como para Windows \r\n
) después de ella.
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 ScannerScanner#nextType
consuma estos separadores de línea. Los leerá System.in
(¿de qué otra manera Scanner sabría que no hay más dígitos del usuario que representen un age
valor que no sean espacios en blanco?) 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 de escáner siempre escanean a partir del texto almacenado en caché.
Ahora Scanner#nextLine()
simplemente recopila y devuelve todos los caracteres hasta que encuentre separadores de línea (o 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é de 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 al final de la secuencia).
Por cierto, nextLine
también consume esos separadores de línea.
Solución
Así que cuando se quiere pedir el número y luego por toda la línea, evitando que cadena vacía como resultado de nextLine
, o bien
- consumir separador de línea dejado por la
nextInt
memoria caché de escáneres
- llamando
nextLine
,
- O la forma más legible de la OMI sería llamar
skip("\\R")
o skip("\r\n|\r|\n")
dejar que el escáner omita la parte que coincida con el separador de línea (más información sobre \R
: https://stackoverflow.com/a/31060125 )
- no use
nextInt
(ni next
, ni ningún nextTYPE
método) en absoluto. En su lugar, lea los datos completos línea por línea usando nextLine
y analice los números de cada línea (suponiendo que una línea contenga solo un número) al tipo apropiado como int
via Integer.parseInt
.
Por cierto : los métodos pueden omitir delimitadores (de forma predeterminada, todos los espacios en blanco, como tabulaciones, separadores de línea), incluidos los almacenados en caché por el escáner, hasta que encuentren el siguiente valor que no sea delimitador (token). Gracias a eso por entrada como códigoScanner#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 adecuadamente num1=42
num2=321
name=foobar
.