Autor Tema: crear agenda en java problema scanner no se para en nextLine caso extraño  (Leído 3546 veces)

JaviRmz

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 2
    • Ver Perfil
Hola a todos, soy nuevo en el foro y necesito su ayuda.

Tengo que crear una agenda con varias opciones, entre ellas esta el de agregar contacto en el cual se almacena en una posicion en el arreglo de objetos que tiene 5 atributos que son nombre, direccion, telefono, correo y edad, todo va bien cuando lo corro, los datos que va a agregando el usuario se guadan, y en el metodo agregarcontacto utilizo una variable bandera "nc" que inicializo en -1 y va aumentando cada vez que agrego un nuevo contacto, para asi que los valores que tomen los atributos se guarden en cada posicion del arreglo. pero el problema es cuando voy a agregar el 4 contacto, el atributo nombre que es el primer atributo no me lo toma en cuenta o sea se lo saltea y no me deja ponerle nombre y se pasa al atributo direccion, es decir el atributo nombre del cuarto contacto que quiero agregar me lo deja en blanco.

Alguien me podria decir cual es mi error o que podria hacer. y gracias por su atencion  :)

codigo:

Clase Contacto

Código: [Seleccionar]
class Contacto
{

    public String nombre;
    private String direccion, telefono, correo;
    private int edad;

    private Scanner sc = new Scanner(System.in);

    public Contacto() {
        this.nombre = "";
        this.direccion = "";
        this.correo = "";
        this.telefono = "";
        this.edad = 0;
    }
   
     
    /*public contacto(){
           
     }*/

    public void entradaDatos() {
        System.out.println("Dame el nombre");
        this.nombre = sc.nextLine();
        System.out.println("Dame la direccion");
        this.direccion = sc.nextLine();
        System.out.println("Dame el telefono");
        this.telefono = sc.nextLine();
        System.out.println("Dame el correo");
        this.correo = sc.nextLine();
        System.out.println("Dame la edad");
        this.edad = sc.nextInt();
    }

    public void mostrarDatos() {
        System.out.println("nombre: " + this.nombre);
        System.out.println("direccion:  " + this.direccion);
        System.out.println("correo:  " + this.correo);
        System.out.println("Telefono:  " + this.telefono);
        System.out.println("Edad:  " + this.edad);
        System.out.println(" ");
    }
}


Clase Proyecto_agenda

Código: [Seleccionar]
public class Proyecto_agenda {

    public static Contacto agenda[];
    public static Contacto aux;
    public static int n = 5;
    public static int nc = -1;
    public static Scanner sc = new Scanner(System.in);

    public static void crearAgenda(Contacto a[]) {
        for (int i = 0; i < a.length; i++) {
            a[i] = new Contacto();
        }
    }

    public static void agregarContacto(Contacto a[]) {
        nc++;
        if (nc < n) {
            a[nc].entradaDatos();
            ordIntercambio(a);
        } else {
            System.out.println("Contactos llenos");
        }
    }

    public static void mostrarAgenda(Contacto a[]) {
        for (int i = 0; i < n; i++) {
            a[i].mostrarDatos();
        }
    }
    /*public static void intercambiar(Contacto[] a, int i, int j) {
     aux = a[j];
     a[j] = a[j+1];
     a[j+1] = aux;
     }*/

    public static void ordIntercambio(Contacto[] a) {
        int i, j;
        for (i = 1; i < a.length; i++) {
            for (j = 0; j < a.length - 1; j++) {
                if ((a[j].nombre).compareTo(a[j + 1].nombre) > 0) {
                    do{
                        aux = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = aux;
                    }while (a[j].nombre!="");
                }
            }
        }
    }
 
       
    public static void menu() {
        int op;
        do {
            System.out.println("Menu AGENDA TELEFONICA ");
            System.out.println("1-. Añadir Contacto");
            System.out.println("2-. Mostrar Contacto");
            System.out.println("9-. Salir");
            System.out.println("opcion -->");
            op = sc.nextInt();
            switch (op) {
                case 1:
                    agregarContacto(agenda);
                    break;
                case 2:
                    mostrarAgenda(agenda);
            }
        } while (op < 9);
    }

    public static void main(String[] args) {
       
        agenda = new Contacto[n];
        crearAgenda(agenda);
        aux = new Contacto();
        menu();

    }

}
« Última modificación: 26 de Enero 2018, 19:42 por Ogramar »

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 988
    • Ver Perfil
Re:crear una agenda en java
« Respuesta #1 en: 17 de Octubre 2017, 13:29 »
La clase Scanner de Java tiene una pequeña peculiaridad que puede ser bastante molesta.

Cada vez que lees por teclado un valor numérico, por ejemplo en:

Citar
this.edad = sc.nextInt();

Para introducir un valor, tu tecleas el valor y luego pulsas la tecla return/intro para que el valor sea recogido por el programa.
Entonces, en el flujo de datos (stream) ahora mismo se encuentran los caracteres de los valores que has introducido, por ejemplo 23, pero también está el caracter que corresponde a la tecla return, que es \r. (También conocido como retorno de carro CR)
Es decir, en el stream tendríamos esto:
Código: [Seleccionar]
23\r
Java, cuando lee números con los métodos adecuados (nextInt(), nextDouble(), nexftFloat(),....)  recoge el valor numérico, pero deja en el flujo de datos el caracter \r porque no es un número, y por lo tanto lo ignora.

Esto significa que después de que hayas leido la edad, en el stream todavía tenemos esto:
Código: [Seleccionar]
\r
El programa sigue su ejecución, con ese \r en el stream, aburrido y esperando a que venga alguien que no lo ignore, que sí quiera leerlo y recogerlo como valor.

El método nextLine(), si que lo recogerá, aunque el caracter \r no tenga representación visual como el resto de caracteres alfanuméricos ('a', 'G', 'h', '1', '%',.. etc), nextLine() lo recogerá y lo sacará del flujo de datos.
Porque de hecho, ese \r es quien le indica a nextLine() que el usuario ha terminado de introducir la "linea de datos" y ya puede recoger lo que se haya tecleado.

Y aquí es donde viene el problema.
Si tras leer la edad, haces una lectura de valores "no numéricos" , por ejemplo el nombre de un nuevo contacto, con nextLine():

Citar
System.out.println("Dame el nombre");
        this.nombre = sc.nextLine();

Este nextLine() se va a encontrar con la situación de que en el stream ya hay un \r, el que había quedado ignorado tras leer la edad.
Y entonces nextLine() se cree que lo que pasa es que el usuario ha pulsado ya la tecla return, cuando en realidad no ha tenido ocasión de teclear nada todavía, y entonces recoge una "cadena vacía" que termina con el caracter \r (que ahora sí es recogido).

Y por lo tanto el programa continua pasa a la siguiente inserción de datos:
Citar
System.out.println("Dame la direccion");
        this.direccion = sc.nextLine();

Este nextLine() si que se encuentra el stream totalmente vacío, así que ahora si que esperará a que el usuario introduzca datos hasta que realmente pulse la tecla return.

Por esto el usuario percibe que Java se ha "saltado" la inserción de datos para el nombre del contacto. Pero en realidad no se la ha saltado, lo que ha ocurrido es que un \r olvidado en el flujo de datos le ha hecho creer que el usuario ha pulsado la tecla intro.


Este error es muy puñetero y un auténtico dolor de cabeza cuando nos iniciamos en la programación en Java. Porque es tan tonto y absurdo, que no somos capaces de entender cuál es el problema.

Afortunadamente, es sencillisimo de solucionar.

Basta con que hacer un nextline() "extra" cuando hayamos terminado de introducir datos numéricos.
Este nextLine() no hace falta que guarde el dato recogido en una variable, simplemente llamamos al método y punto.
Citar
this.edad = sc.nextInt();
sc.nextLine();
Así, tras leer la edad y guardarla en la variable edad, quedará un \r en el flujo de datos.
Pero el siguiente nextLine() lo recogerá y como no está asignado a ninguna variable, simplemente el \r desaparece y ya tenemos el stream limpito para no tener problemas en la próxima lectura de datos.

Pruébalo, pon un nextLine() extra tras cada lectura de datos numéricos.
Por ejemplo también en el menú:

Citar
System.out.println("9-. Salir");
System.out.println("opcion -->");
op = sc.nextInt();
sc.nextLine();//Para limpiar el flujo de datos
            switch (op) {
                case 1:


Seguro que con esto evitas este problema.

Otra forma de evitarlo es utilizar únicamente el método nextLine() para recoger datos y luego, si quieres valores numéricos, parsearlo, por ejemplo.
En lugar de esto:
Citar
this.edad = sc.nextInt();
Hacemos esto:
Citar
String datoEdad= sc.nextLine();
this.edad = Integer.parseInt(datoEdad);

Así tienes un valor numérico y evitas dejar \r ignorados en el flujo de datos.

Son dos formas distintas de esquivar este problema, no hay una mejor que otra. Yo suelo usar la primera forma, la de meter nextLine() extra tras leer números.

Comentanos si esto te ha solucionado el problema, de lo contrario, buscaremos otro motivo.
Un saludo

« Última modificación: 17 de Octubre 2017, 13:34 por Kabuto »
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

JaviRmz

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 2
    • Ver Perfil
Re:crear una agenda en java
« Respuesta #2 en: 18 de Octubre 2017, 04:41 »
Excelente muchas gracias, ya había resuelto el problema utilizando Buffered para pedir los datos pero es bueno saber que hacer para la próxima, esto le servirá de mucho a una amiga gracias  ;D

 

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".