¿El escáner se salta nextLine() después de usar next() o nextFoo()?

Resuelto blekione asked hace 12 años • 25 respuestas

Estoy usando los Scannermé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.

blekione avatar Oct 27 '12 23:10 blekione
Aceptado

Esto se debe a que el Scanner.nextIntmétodo no lee el carácter de nueva línea en la entrada creada al presionar "Enter", por lo que la llamada regresa Scanner.nextLinedespués de leer esa nueva línea .

Encontrará un comportamiento similar cuando utilice Scanner.nextLineafter Scanner.next()o cualquier Scanner.nextFoométodo (excepto nextLineél mismo).

Solución alterna:

  • Realice una Scanner.nextLinellamada después de cada una Scanner.nextInto Scanner.nextFooconsuma 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.nextLiney conviértala al formato adecuado que necesite. Por ejemplo, puede convertir a un número entero usando Integer.parseInt(String)el método.

    int option = 0;
    try {
        option = Integer.parseInt(input.nextLine());
    } catch (NumberFormatException e) {
        e.printStackTrace();
    }
    String str1 = input.nextLine();
    
Rohit Jain avatar Oct 27 '2012 16:10 Rohit Jain

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();
Prine avatar Aug 14 '2011 12:08 Prine

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()

Bohemian avatar Aug 14 '2011 12:08 Bohemian

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

Denis Tulskiy avatar Mar 23 '2014 16:03 Denis Tulskiy

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.

skipusa expresiones regulares donde

  • \Rrepresenta separadores de línea
  • ?lo hará \Ropcional, lo que evitará skipque 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.
    • tirar java.util.NoSuchElementExceptionen caso de
      • fuente de datos cancelada/cerrada,
      • o cuando los datos existentes no coinciden con lo que queremos omitir

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.inla 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 agey 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#nextTypeSystem.inage

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, nextLinetambié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 nextIntla caché de los escáneres por
  • llamando nextLine,
  • O, en mi opinión , una forma más legible sería llamar skip("\\R")o skip("\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(ni next, ni ningún nextTYPEmétodo) en absoluto. En su lugar, lea los datos completos línea por línea usando nextLiney analice los números de cada línea (asumiendo que una línea contiene solo un número) para escribirlos correctamente, como intvia Integer.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.

Pshemo avatar Oct 09 '2016 22:10 Pshemo