Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: xassiz~ en 20 Febrero 2011, 17:02 pm



Título: [Solucionado] Problema con lista enlazada en C (buffer de entrada)
Publicado por: xassiz~ en 20 Febrero 2011, 17:02 pm
#Solucion del post (http://foro.elhacker.net/programacion_cc/solucionado_problema_con_lista_enlazada_en_c-t320045.0.html;msg1583532#msg1583532)

Bueno, estaba practicando con listas enlazadas en C, pero no se porque no me va esto :xD

El caso es que no me deja ingresar bien los datos, ¿uso mal fgets() (http://www.cplusplus.com/reference/clibrary/cstdio/fgets/)?

Código
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4.  
  5. struct _contacto {
  6.    char nombre[33];
  7.    char telefono[13];
  8.    struct _contacto *siguiente;
  9. };
  10. struct _contacto *primero, *ultimo;
  11.  
  12. void agregar_contacto()
  13. {
  14.     struct _contacto *nuevo;
  15.     nuevo = (struct _contacto*)malloc(sizeof(struct _contacto));
  16.     if(nuevo==NULL){
  17.         printf("\nNo hay espacio suficiente.");
  18.         return;
  19.     }
  20.     printf("\n\nAgregar contacto\n");
  21.     printf("------------------");
  22.     printf("\nNombre: ");
  23.     fgets(nuevo->nombre,32,stdin);
  24.     printf("\nTelefono: ");
  25.     fgets(nuevo->telefono,12,stdin);
  26.     nuevo->siguiente = NULL;
  27.     if(primero==NULL){
  28.         primero = nuevo;
  29.         ultimo = nuevo;
  30.     } else {
  31.         ultimo->siguiente = nuevo;
  32.         ultimo = nuevo;
  33.     }
  34. }
  35.  
  36. void buscar_contacto_tlfn()
  37. {
  38.     struct _contacto *busqueda, *actual;
  39.     busqueda = (struct _contacto*)malloc(sizeof(struct _contacto));
  40.     if(busqueda==NULL){
  41.         printf("\nNo hay espacio suficiente.");
  42.         return ;
  43.     }
  44.     printf("\n\nBuscar contacto por telefono\n");
  45.     printf("----------------------------");
  46.     printf("\nTelefono: ");
  47.     fgets(busqueda->telefono,12,stdin);
  48.     actual = primero;
  49.     while(actual!=NULL){
  50.         if(strcmp(actual->telefono,busqueda->telefono)==0){
  51.             printf("+Nombre: %s\n", actual->nombre);
  52.             return;
  53.         }
  54.         actual = actual->siguiente;
  55.     }
  56.     printf("No encontrado.");
  57. }
  58.  
  59.  
  60. int opcion = 0;
  61.  
  62. void mostrar_menu()
  63. {
  64.     opcion = 0;
  65.     printf("\n\n\n MENU\n");
  66.     printf("======\n");
  67.     printf("1. Agregar contacto\n");
  68.     printf("2. Buscar contacto por telefono\n");
  69.     printf("3. Salir\n");
  70.     scanf("%i", &opcion);
  71. }
  72.  
  73. int main()
  74. {
  75.    while(opcion!=3){
  76.        mostrar_menu();
  77.        switch(opcion)
  78.        {
  79.            case 1:
  80.                agregar_contacto();
  81.                break;
  82.            case 2:
  83.                buscar_contacto_tlfn();
  84.                break;
  85.            default:
  86.                break;
  87.        }
  88.    }
  89.    return 0;
  90. }
  91.  

Saludos!


Título: Re: Problema con lista enlazada en C
Publicado por: Shut en 20 Febrero 2011, 19:45 pm
Hola, bueno lo primero que tienes que incluir la libreria <stdlib.h> para usar la malloc. El problema que sugieres al intentar meter los nombres, es porque el fgets suele dar problemas.Es porque se queda en el buffer del teclado el intro y por eso te salta una opcion del menu.Deberias poner antes de cada fgets, fflush(stdin) para limpiar el buffer y que no te salte.


Título: Re: Problema con lista enlazada en C
Publicado por: xassiz~ en 20 Febrero 2011, 19:55 pm
s porque se queda en el buffer del teclado el intro y por eso te salta una opcion del menu.Deberias poner antes de cada gets, fflush(stdin) para limpiar el buffer y que no te salte.
Gracias, funciona, pero en este post fijado (http://foro.elhacker.net/programacion_cc/lo_que_no_hay_que_hacer_en_cc_nivel_basico-t277729.0.html) dicen que fflush(stdin); es mala costumbre y que lo del buffer debería darme problemas con scanf() pero no con fgets() :-X



Título: Re: Problema con lista enlazada en C
Publicado por: Shut en 20 Febrero 2011, 20:05 pm
Bueno pues entonces si no la quieres usar puedes probar lo del post, es una solucion mejor parece ser, es comprobar si se quedo almacenado un salto de linea en esa posicion, y si es asi mete un nulo. Puedes probar, y si funciona pues mejor. Sino siempre quedara fflush(stdin) xdd

Voi a comprobar yo si me funciona con lo de poner el nulo  :)


Título: Re: Problema con lista enlazada en C
Publicado por: Leber en 20 Febrero 2011, 20:31 pm
Puedes meter un '\0' directamente. Así es como yo lo hago.

Código:
#include <stdio.h>
#include <string.h>

int
main(int argc, char **argv)
{

        char test[20];

                fgets(test, sizeof(test)-1, stdin);
                test[strlen(test)-1] = '\0';
                printf("%s\n", test);
                return 0;
}


Título: Re: Problema con lista enlazada en C
Publicado por: xassiz~ en 20 Febrero 2011, 20:44 pm
Pero lo de cambiar el salto por línea por el caracter nulo sería en el momento de elegir la opción en el menú, ¿no?

En ese caso tengo que reemplazar el scanf() por fgets() y poner la variable opcion como un arreglo de caracteres :-X


Título: Re: Problema con lista enlazada en C
Publicado por: D4RIO en 21 Febrero 2011, 00:15 am
Usa readline de GNU


Título: Re: Problema con lista enlazada en C
Publicado por: Littlehorse en 21 Febrero 2011, 00:47 am
Las variables globales son perjudiciales para la salud, trata de evitarlas siempre que sea posible.


 
En ese caso tengo que reemplazar el scanf() por fgets() y poner la variable opcion como un arreglo de caracteres :-X
 

Efectivamente. La forma mas segura de realizar un ingreso de datos por parte del usuario es leyendo como texto y luego parseando y validando lo necesario.

Saludos


Título: Re: Problema con lista enlazada en C
Publicado por: xassiz~ en 21 Febrero 2011, 19:06 pm
Bueno, ya me deshice de variables globales y de los scanf(), pero curiosamente sigue pasando el mismo error :xD

Código
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4.  
  5. struct _contacto {
  6.    char nombre[33];
  7.    char telefono[13];
  8.    struct _contacto *siguiente;
  9. };
  10. struct _contacto *primero, *ultimo;
  11.  
  12. void agregar_contacto()
  13. {
  14.     struct _contacto *nuevo;
  15.     nuevo = (struct _contacto*)malloc(sizeof(struct _contacto));
  16.     if(nuevo==NULL){
  17.         printf("\nNo hay espacio suficiente.");
  18.         return;
  19.     }
  20.     printf("\n\nAgregar contacto\n");
  21.     printf("------------------");
  22.     printf("\nNombre: ");
  23.     fgets(nuevo->nombre,32,stdin);
  24.     printf("\nTelefono: ");
  25.     fgets(nuevo->telefono,12,stdin);
  26.     nuevo->siguiente = NULL;
  27.     if(primero==NULL){
  28.         primero = nuevo;
  29.         ultimo = nuevo;
  30.     } else {
  31.         ultimo->siguiente = nuevo;
  32.         ultimo = nuevo;
  33.     }
  34. }
  35.  
  36. void buscar_contacto_tlfn()
  37. {
  38.     struct _contacto *busqueda, *actual;
  39.     busqueda = (struct _contacto*)malloc(sizeof(struct _contacto));
  40.     if(busqueda==NULL){
  41.         printf("\nNo hay espacio suficiente.");
  42.         return ;
  43.     }
  44.     printf("\n\nBuscar contacto por telefono\n");
  45.     printf("----------------------------");
  46.     printf("\nTelefono: ");
  47.     fgets(busqueda->telefono,12,stdin);
  48.     actual = primero;
  49.     while(actual!=NULL){
  50.         if(strcmp(actual->telefono,busqueda->telefono)==0){
  51.             printf("+Nombre: %s\n", actual->nombre);
  52.             return;
  53.         }
  54.         actual = actual->siguiente;
  55.     }
  56.     printf("No encontrado.");
  57. }
  58.  
  59.  
  60. char mostrar_menu()
  61. {
  62.     char opcion[3];
  63.     printf("\n\n\n MENU\n");
  64.     printf("======\n");
  65.     printf("1. Agregar contacto\n");
  66.     printf("2. Buscar contacto por telefono\n");
  67.     printf("3. Salir\n");
  68.     fgets(opcion,2,stdin);
  69.     return opcion[0];
  70. }
  71.  
  72. int main()
  73. {
  74.    char opcion='0';
  75.    while(opcion!='3'){
  76.        opcion = mostrar_menu();
  77.        if(opcion=='1')
  78.            agregar_contacto();
  79.        else if(opcion=='2')
  80.            buscar_contacto_tlfn();
  81.    }
  82.    return 0;
  83. }
  84.  

Saludos!


Título: Re: Problema con lista enlazada en C
Publicado por: Littlehorse en 21 Febrero 2011, 19:56 pm
Es que de la forma que haces la lectura, fgets nunca llega a leer el salto de linea por lo tanto nada puede hacer con el.

Si tenes una cadena de 3 posiciones, y a fgets le pasas como parámetro que lea solo 2, en realidad lee 2-1. Es decir, lee 1 solo carácter, luego agrega el carácter nulo a la cadena, y el salto de linea queda deambulando por el buffer de entrada.


Citar
Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or a the End-of-File is reached, whichever comes first.
A newline character makes fgets stop reading, but it is considered a valid character and therefore it is included in the string copied to str.
 A null character is automatically appended in str after the characters read to signal the end of the C string.
Cuando dije leer como texto me referia a leer y analizar una expresion para verificar si el usuario ha ingresado la opción correctamente. Lo que estas haciendo ahora es utilizar una cadena como si fuese un solo carácter y aunque es legal no es del todo correcto.

Saludos!


Título: Re: Problema con lista enlazada en C
Publicado por: xassiz~ en 21 Febrero 2011, 20:45 pm
Lo solucioné así:
Código
  1. char mostrar_menu()
  2. {
  3.     char opcion[2];
  4.     printf("\n\n\n MENU\n");
  5.     printf("======\n");
  6.     printf("1. Agregar contacto\n");
  7.     printf("2. Buscar contacto por telefono\n");
  8.     printf("3. Salir\n");
  9.     fgets(opcion,3,stdin);
  10.     return opcion[0];
  11. }

Pero me falla cuando elijo la opción "Salir" (3). No entiendo porqué funciona en las primeras y en esta no :-X

Dices que lo que hago no es del todo correcto... ¿tú como lo solucionarías?

Saludos!


Título: Re: Problema con lista enlazada en C
Publicado por: Littlehorse en 21 Febrero 2011, 22:01 pm
Ahora tienes una cadena de 2 posiciones y a fgets le pasas 3 como tamaño por lo tanto lee 2 caracteres, por ende es posible que la cadena te quede sin el carácter nulo.

Si el problema es solo el contenido restante en el buffer de entrada, en Windows se puede tirar de la API para solucionarlo fácilmente, pero para no salir del estándar puedes hacer algo como esto:

Código
  1. void CleanStdin()
  2. {
  3. while(getchar() != '\n');
  4. }
  5.  

Aunque no debería hacer falta puesto que el salto de linea debería quedar perfectamente en la cadena pasada a fgets siempre que le dejes espacio. Luego puedes reemplazarlo fácilmente por un carácter nulo.

Con el tema de las lecturas, hay miles de formas de leer una opción. Usualmente cuando son opciones simples con fgets + sscanf basta y sobra:


Código
  1. if(fgets(Buff, sizeof Buff, stdin))
  2. sscanf(Buff, "%d", &Opt);
  3.  




El problema del buffer de entrada en Windows esta muy tratado en el foro. Hoy ya vi 3 posts con la misma duda asi que mi recomendación es que utilicen el buscador del foro, ya que hay hilos enteros solo sobre este tema. Tambien con Google pueden buscar algo como:

Citar
fflush(stdin) site:foro.elhacker.net

o

Citar
fgets problema site:foro.elhacker.net

Saludos


Título: Re: Problema con lista enlazada en C
Publicado por: xassiz~ en 21 Febrero 2011, 22:22 pm
Bueno, gracias por todo, si al final era más simple de lo que parecía :rolleyes:

Código
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4.  
  5. struct _contacto {
  6.    char nombre[33];
  7.    char telefono[13];
  8.    struct _contacto *siguiente;
  9. };
  10. struct _contacto *primero, *ultimo;
  11.  
  12. void agregar_contacto()
  13. {
  14.     struct _contacto *nuevo;
  15.     nuevo = (struct _contacto*)malloc(sizeof(struct _contacto));
  16.     if(nuevo==NULL){
  17.         printf("\nNo hay espacio suficiente.");
  18.         return;
  19.     }
  20.     printf("\n\nAgregar contacto\n");
  21.     printf("------------------");
  22.     printf("\nNombre: ");
  23.     fgets(nuevo->nombre,32,stdin);
  24.     printf("\nTelefono: ");
  25.     fgets(nuevo->telefono,12,stdin);
  26.     nuevo->siguiente = NULL;
  27.     if(primero==NULL){
  28.         primero = nuevo;
  29.         ultimo = nuevo;
  30.     } else {
  31.         ultimo->siguiente = nuevo;
  32.         ultimo = nuevo;
  33.     }
  34. }
  35.  
  36. void buscar_contacto_tlfn()
  37. {
  38.     struct _contacto *busqueda, *actual;
  39.     busqueda = (struct _contacto*)malloc(sizeof(struct _contacto));
  40.     if(busqueda==NULL){
  41.         printf("\nNo hay espacio suficiente.");
  42.         return ;
  43.     }
  44.     printf("\n\nBuscar contacto por telefono\n");
  45.     printf("----------------------------");
  46.     printf("\nTelefono: ");
  47.     fgets(busqueda->telefono,12,stdin);
  48.     actual = primero;
  49.     while(actual!=NULL){
  50.         if(strcmp(actual->telefono,busqueda->telefono)==0){
  51.             printf("+Nombre: %s\n", actual->nombre);
  52.             return;
  53.         }
  54.         actual = actual->siguiente;
  55.     }
  56.     printf("No encontrado.");
  57. }
  58.  
  59.  
  60. void mostrar_menu()
  61. {
  62.     printf("\n\n\n MENU\n");
  63.     printf("======\n");
  64.     printf("1. Agregar contacto\n");
  65.     printf("2. Buscar contacto por telefono\n");
  66.     printf("3. Salir\n");
  67. }
  68.  
  69. int main()
  70. {
  71.    int opcion = 0;
  72.    char bufferOpcion[5];
  73.    while(opcion!=3){
  74.        mostrar_menu();
  75.        fgets(bufferOpcion,sizeof(bufferOpcion),stdin);
  76.        sscanf(bufferOpcion, "%d", &opcion);
  77.        switch(opcion){
  78.            case 1:
  79.                agregar_contacto();
  80.                break;
  81.            case 2:
  82.                buscar_contacto_tlfn();
  83.                break;
  84.            default:
  85.                break;
  86.        }
  87.    }
  88.    return 0;
  89. }
  90.  

Saludos!