Problema con registros

Iniciado por jaxoR, 4 Septiembre 2014, 01:54 AM

0 Miembros y 1 Visitante están viendo este tema.

jaxoR

Hola, tengo un problema al leer un archivo en el que guardo un registro. El problema es que me muestra varios signos y numeros que yo no ingrese. Les paso a dejar las funciones en donde escribo y leo el fichero (Paso el fichero y el archivo por referencia):

Código (cpp) [Seleccionar]
void DarDeAlta(registro *clientes, int n, FILE **db)
{
   int a = 1;

   while (a == 1)
   {
       printf("Ingrese un nombre: ");
       scanf("%s", ((*clientes).nombre));
       fprintf(*db, "%s\n", (*clientes).nombre);

       printf("Ingrese su apellido: ");
       scanf("%s", ((*clientes).apellido));
       fprintf(*db, "%s\n", (*clientes).apellido);

       printf("Ingrese el telefono: ");
       scanf("%d", &(*clientes).telefono);
       fprintf(*db, "%d\n", (*clientes).telefono);

       printf("Ingrese la edad: ");
       scanf("%d", &(*clientes).edad);
       fprintf(*db, "%d\n", (*clientes).edad);

       Seguir(&a);
   }

   fclose(*db);
   return;
}

void MostrarClientes(registro *clientes, int n, FILE **db)
{
   *db = fopen("clientes.dat", "r");
   if(*db == NULL)
   {
       printf("El archivo no existe\n");
   }
   else{
       while(feof(*db) == 0)
       {
           fgets((*clientes).apellido, 30, *db);
           printf("%s\n", (*clientes).nombre);

           fgets((*clientes).apellido, 30, *db);
           printf("%s\n", (*clientes).apellido);

           fscanf(*db, "%d", &(*clientes).telefono);
           printf("%d\n", (*clientes).telefono);

           fscanf(*db, "%d", &(*clientes).edad);
           printf("%d\n", (*clientes).edad);
       }
   }

   fclose(*db);
   return;
}

rir3760

Si no tienes un curso o libro de calidad deberías conseguir uno, para recomendaciones basta con utilizar el motor de búsqueda de los foros.

Deficiencias en el fragmento hay bastantes:

* Falta consistencia: una función cierra el archivo, la otra lo abre y cierra. Bien ambas funciones operan sobre el archivo (lo abren y cierran) o ambas solo realizan la operación en turno.

* No es necesario emular el paso por referencia.

* En la función "DarDeAlta" la ultima sentencia esta de mas (las funciones terminan con su llave de cierre), hay que cambiar la función "Seguir" para utilizar su valor de retorno y una vez hecho lo anterior se cambia el bucle por uno do ... while eliminando de paso la variable local "a".

* En la función "MostrarClientes" los dos últimos parámetros no son necesarios: "n" no es utilizado en la función mientras que lo primero que haces con "db" es sobrescribir su valor, este ultimo se debe cambiar por una variable local.

* Hay un error lógico en la función "MostrarClientes":
fgets((*clientes).apellido, 30, *db); /* Leemos el apellido    */
printf("%s\n", (*clientes).nombre);   /* Imprimimos el nombre  */


* En ambas funciones utilizas la notación "(*variable).campo" cuando puedes acortar evitando los paréntesis con "variable->campo".

* El error que mencionas se debe al uso intercalado de fgets y fscanf en la función "MostrarClientes". Para explicarlo mejor etiquetamos los pasos en cada iteracion:
while (feof(*db) == 0){
   fgets((*clientes).apellido, 30, *db);     /* 1 */
   printf("%s\n", (*clientes).nombre);
   
   fgets((*clientes).apellido, 30, *db);     /* 2 */
   printf("%s\n", (*clientes).apellido);
   
   fscanf(*db, "%d", &(*clientes).telefono); /* 3 */
   printf("%d\n", (*clientes).telefono);
   
   fscanf(*db, "%d", &(*clientes).edad);     /* 4 */
   printf("%d\n", (*clientes).edad);
}


Primera iteración:

1 fgets lee el nombre (ya se menciono que hay un error lógico).
2 fgets lee el apellido.
3 fscanf lee el telefono, deja el ultimo carácter invalido (el avance de linea) en el bufer correspondiente.
3 fscanf lee la edad, deja el ultimo carácter invalido (el avance de linea) en el bufer correspondiente.

Segunda iteración:

1 fgets lee el '\n' como una linea en blanco y se almacena esta en el nombre.
2 fgets lee el nombre y lo almacena en el apellido.
3 fscanf falla al tratar de leer el telefono del apellido.
4 fscanf falla al tratar de leer la edad del apellido.

Tercera iteración

1 fgets lee el nombre (apellido del registro anterior)
2 fgets lee el apellido (telefono del registro anterior)
etc.

Para solucionarlo se tienen dos opciones:

A) Leer todas las lineas del archivo con fgets, de ser necesario (por ejemplo en este caso con el teléfono y la edad) se utiliza un array de caracteres auxiliar y la función sscanf para extraer el valor.

B) Justo después de la llamada a fscanf y antes de la llamada a fgets se descarta el resto de la linea con un bucle, tomando tu caso como ejemplo:
while (feof(*db) == 0){
   fgets((*clientes).apellido, 30, *db);
   printf("%s\n", (*clientes).nombre);
   
   /* ... */
   
   fscanf(*db, "%d", &(*clientes).edad);
   printf("%d\n", (*clientes).edad);
   
   {
      int ch;
     
      while ((ch = fgetc(*db)) != EOF && ch != '\n')
         ;
   }
}


Por ultimo en la función "MostrarClientes" se debe evitar el uso de la función "feof" ya que esto resultara en un iteración de mas, en su lugar hay que verificar el valor de retorno de las funciones fgets y fscanf.

Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

jaxoR

rir, gracias como siempre. Resolví todos los errores pero no entendí lo siguiente:

* En ambas funciones utilizas la notación "(*variable).campo" cuando puedes acortar evitando los paréntesis con "variable->campo".

No entiendo a que te refieres con: variable->campo

Y lo otro que no entendí es lo siguiente:

Código (cpp) [Seleccionar]
while (feof(*db) == 0){
   fgets((*clientes).apellido, 30, *db);
   printf("%s\n", (*clientes).nombre);

   /* ... */

   fscanf(*db, "%d", &(*clientes).edad);
   printf("%d\n", (*clientes).edad);

   {
      int ch;

      while ((ch = fgetc(*db)) != EOF && ch != '\n')
         ;
   }
}


No entiendo que hace el segundo while y cuando entraría adentro de esas llaves

rir3760

Cita de: wiD^ en  4 Septiembre 2014, 22:50 PMno entendí lo siguiente:

* En ambas funciones utilizas la notación "(*variable).campo" cuando puedes acortar evitando los paréntesis con "variable->campo".

No entiendo a que te refieres con: variable->campo
En C "->" es un operador y se utiliza para en base a un puntero a estructura acceder al campo indicado. Donde sea que utilices "(*puntero).nombre_del_campo" puedes sustituirlo por "puntero->nombre_del_campo" con el mismo resultado.

Tomando tu programa como ejemplo puedes cambiar esta parte:
fgets((*clientes).apellido, 30, *db);
printf("%s\n", (*clientes).nombre);

A:
fgets(clientes->apellido, 30, *db);
printf("%s\n", clientes->nombre);


Cita de: wiD^ en  4 Septiembre 2014, 22:50 PMNo entiendo que hace el segundo while
Ese bucle lee un carácter y lo descarta (lee otro) hasta encontrarse con uno que sea igual a EOF (indica error) o '\n' (indica el final de la linea). El objetivo de ello es descartar el resto de la linea ya que fscanf no lee por linea (fgets si y ahí empiezan los problemas que ya comente).

Cita de: wiD^ en  4 Septiembre 2014, 22:50 PMy cuando entraría adentro de esas llaves
Siempre. Puedes tener un bloque delimitado por llaves en cualquier lugar donde una sentencia sea valida y no hay problema, el objetivo de utilizar un bloque es evitar un conflicto de nombres si, por alguna razón, ya existe una variable con el nombre "ch".

Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language