[ Foro de C ]

Capítulo 6: Manejo de ficheros. Ej. 6.10a ...HELP!

07-Aug-2012 18:04
Pablo Rampa
7 Respuestas

El código funciona. Pero al cerrarlo y abrir nuevamente se van corriendo de lugar las fichas hasta que se pierden. Creo que me pasa como a Antonio en el Ej. 9.2 que es la misma agenda, pero
con punteros.
Este es el código (de paso, no sé si he interpretado bien lo que
el Profesor pretende con tal ejercicio):

6.10a_ AGENDA (3a.versión). Mejorar la última versión de la agenda anterior(6.8a, la que usa
fwrite, fread y sizeof) para que no lea todas las fichas a la vez, sino que lea una única ficha del
disco cada vez que lo necesite, saltando a la posición en que se encuentra dicha ficha con “fseek”.

#include <stdio.h>
#include <string.h>

int main()      
{          
  struct agenda
  {      
     char nombre [21];
     char direccion [31];
     char celular[13];
     char email[31];
     unsigned short int dia, mes, anyo;
  }persona[100];

  FILE* fichero;      
  char textoAux[21];
  int opcion, personas=0;          
  int i, j, numFicha;              
                   
  fichero = fopen("agenda.dat", "rb");
  if (fichero != NULL)
  {
      while (! feof(fichero))
     {                          
        fread(&persona[i],sizeof(persona), 1, fichero);          
        printf("%s\n", persona[i].nombre);        
        printf("%s\n", persona[i].direccion);  
        printf("%s\n", persona[i].celular);
        printf("%s\n", persona[i].email);
        if (feof(fichero)) break;
        printf("%hd %hd %hd\n\n", persona[i].dia, persona[i].mes, persona[i].anyo);
        personas ++;
        if (feof(fichero)) break;
        else
           fread(&persona[i],sizeof(persona), 1, fichero);
     }
   
     fclose(fichero);                
  }
           
  do    
  {
     puts("\n\tMENU:\n");
     puts("1.- Agregar un nuevo dato.");
     puts("2.- Ver todos los datos existentes.");
     puts("3.- Ver todos los datos de una persona.");
     puts("0.- Terminar.");
     puts("   Elija una opcion: ");
       
     scanf("%d", &opcion);
     getchar();                          

     switch (opcion)
     {
        case 1:    /* Agregar un nuevo dato */
           printf("Nombre: ");
           gets(persona[personas].nombre);
           printf ("Direccion: ");
           gets (persona[personas].direccion);
           printf ("Celular: ");
           gets (persona[personas].celular);
           printf ("Correo electronico: ");
           gets (persona[personas].email);
           printf ("Dia de nacimiento: ");
           scanf("%hd", &persona[personas].dia);
           printf ("Mes de nacimiento: ");
           scanf ("%hd", &persona[personas].mes);
           printf ("Año de nacimiento: ");
           scanf ("%hd", &persona[personas].anyo);
           personas ++;
           fwrite(&persona[i], sizeof(persona), 1, fichero);
           break;

        case 2:    /* Ver todos los nombres */
           j = 0;
           puts ("Ver todos los nombres existentes:");
           for (i=0; i<personas; i++)
           {
              printf("%d.- %s\n", i+1, persona[i].nombre);
              j ++;
           }
           if (j == 0) printf("No hay ningun nombre guardado.\n");
           else
              if (j == 1) printf("Solo hay un nombre guardado.\n");
                 else
                    printf("Hay %d nombres guardados.\n", personas);
           break;
               
        case 3:    /* Ver todos los datos de una persona */
           printf("Numero de ficha a leer? ");
           scanf("%d", &numFicha);
           if ((numFicha >0) && (numFicha <= personas))
           {        
              i=numFicha-1;
              fseek(fichero, sizeof(persona)*(i), SEEK_SET);
              fread(&persona[i], sizeof(persona), 1,  fichero);
              fflush(stdin);  
              printf("%d.- %s %s %s %s %hd %hd %hd\n\n", i+1, persona[i].nombre, persona[i].direccion,
                 persona[i].celular, persona[i].email, persona[i].dia, persona[i].mes, persona[i].anyo);
           }
           else
              printf("Escriba un numero de ficha valido.");      
           break;
        }
      }
      while (opcion != 0);  
      fichero = fopen("agenda.dat", "ab");    
      for (i=0; i<personas; i++)    
         fwrite(&persona[i], sizeof(persona), 1, fichero);
           
      fclose(fichero);
      return 0;
                           
}

Si alguien de vosotros tiene a bien contestar, lo agradeceré
mucho.
             ...Saludos.





07-Aug-2012 20:24
Antonio Rodriguez

Hola Pablo!

Me temo que nuestro profesor debe estar disfrutando de unas merecidas vacaciones, así que mientras está ausente a ver si te puedo echar una mano.

Respecto al código que has puesto te comento un par de cosas echándole un vistazo por encima:

fichero = fopen("agenda.dat", "rb");
  if (fichero != NULL)
  {
      while (! feof(fichero))
      {                          
        fread(&persona[i],sizeof(persona), 1, fichero);          
        printf("%s\n", persona[i].nombre);        
        printf("%s\n", persona[i].direccion);  
        printf("%s\n", persona[i].celular);
        printf("%s\n", persona[i].email);
        if (feof(fichero)) break;
        printf("%hd %hd %hd\n\n", persona[i].dia, persona[i].mes, persona[i].anyo);
        personas ++;
        if (feof(fichero)) break;
        else
            fread(&persona[i],sizeof(persona), 1, fichero);
      }
   
      fclose(fichero);                
  }

En esta parte abres el fichero agenda.dat (si existe) y cargas los datos en el struct, pero te falta el "for" para contar las "[i]", de hecho el programa me falla cuando lo ejecuto por segunda vez por este motivo


En la opcion 3 utilizas un "fseek" para leer del fichero, pero recuerda que primero debes abrirlo en modo lectura.

Finalmente, al final vuelves a guardar los datos del struct abriendo el fichero en modo "ab". Esto entiendo que puede ser un problema, ya que (si no me equivoco) te volverá a escribir todo el struct al final de los datos que ya tengas en el fichero, con lo que se duplicarán los datos del fichero cada vez que salgas del programa. Si lo quieres hacer así creo que la opcion adecuada seria "wb" para que te sobreescriba el fichero con los datos del struct.

No me paro mucho más, ya que yo he entendido el ejercicio de otra forma: al inicio del programa no se deben cargar todos los datos en el struct, si no que cada vez que accedes a una opción se debe abrir el fichero, ir a la posición deseada con fseek y ejecutar la orden (leer datos, escribir, etc)

Un saludo!


08-Aug-2012 03:06
Pablo Rampa

Gracias Antonio. Enhorabuenas por el Profesor. Voy a corregir lo
que me has apuntado. Lo último no lo entiendo; si no cargo los datos en el struct, no se presentan todas las fichas guardadas al
abrir el archivo. Pensaba que era importante verlas al principio.
Pero tienes razón porque esa es una diferencia con la agenda anterior.
Tú vas muy adelantado y francamente no puedo ayudarte con tu
ejercicio. No te quise responder en tu consulta (al menos para animarte)para no entorpecer las espectativas de respuesta de parte del profesor. Porque si veía que ya tenías una respuesta, podría tardarse más en acudir en tu auxilio. O no sé si es alcontrario???

Gracias de nuevo, Antonio. Y permitámosle un merecido descanso al
Maestro.
             Saludos.


08-Aug-2012 13:54
Nacho Cabanes (+83)

Estoy de vacaciones, pero sin irme de viaje a ningún sitio, que con los continuos recortes de los sucesivos gobiernos españoles, los profesores no llegamos a final de mes... y lo grave es que aún tenemos que dar gracias porque al menos seguimos con trabajo... así que iré contestando en breve.

Pero es que ese supuesto "tiempo libre extra" que supone el no estar dando clases presenciales, lo estoy dedicando a la familia y a las cosas pendientes que no tengo tiempo de hacer durante el curso escolar, como revisar el curso de C (la mayoría de correcciones ya están publicadas en la web) para hablar de los cambios que proponen los estándares C99 y C11.

Esta noche quiero intentar sacar un rato para leer los últimos mensajes del foro, probar vuestras últimas aportaciones y contaros qué veo bien y qué veo mejorable en ellas. Y si no fuera hoy, como muy tarde sería mañana.  ;-)


08-Aug-2012 14:42
Pablo Rampa

Un saludo cordial, Profesor. No se preocupe. Despacio...que voy
de prisa! :-)
Antonio me hechó una mano, pero todavía no entiendo bien lo que
quiere en el Ej. 6.10a cuando se refiere a que "no lea todas las fichas a la vez, sino que lea una única ficha del disco cada vez que lo necesite". Le quité la parte que lee todo y guarda en el struct, al principio. Creo que estoy perdido. Lo hice así:
/*6.10a_ AGENDA (3a.versión). Mejorar la última versión de la agenda anterior(6.8a, la que usa
fwrite, fread y sizeof) para que no lea todas las fichas a la vez, sino que lea una única ficha del
disco cada vez que lo necesite, saltando a la posición en que se encuentra dicha ficha con “fseek”.*/

#include <stdio.h>
#include <string.h>

int main()      
{          
  struct agenda
  {      
     char nombre [21];
     char direccion [31];
     char celular[13];
     char email[31];
     unsigned short int dia, mes, anyo;
  }persona[100];

  FILE* fichero;      
  char textoAux[21];
  int opcion, personas=0;          
  int i, j, numFicha;              
           
  do    
  {
     puts("\n\tMENU:\n");
     puts("1.- Agregar un nuevo dato.");
     puts("2.- Ver todos los datos existentes.");
     puts("3.- Ver todos los datos de una persona.");
     puts("0.- Terminar.");
     puts("   Elija una opcion: ");
       
     scanf("%d", &opcion);
     getchar();                          

     switch (opcion)
     {
        case 1:    /* Agregar un nuevo dato */
           printf("Nombre: ");
           gets(persona[personas].nombre);
           printf ("Direccion: ");
           gets (persona[personas].direccion);
           printf ("Celular: ");
           gets (persona[personas].celular);
           printf ("Correo electronico: ");
           gets (persona[personas].email);
           printf ("Dia de nacimiento: ");
           scanf("%hd", &persona[personas].dia);
           printf ("Mes de nacimiento: ");
           scanf ("%hd", &persona[personas].mes);
           printf ("Anyo de nacimiento: ");
           scanf ("%hd", &persona[personas].anyo);
           personas ++;
           fwrite(&persona[i], sizeof(persona), 1, fichero);
           break;

        case 2:    /* Ver todos los nombres */
           j = 0;
           puts ("Ver todos los nombres existentes:");
           fichero = fopen("agenda.dat", "rb");
           if (fichero != NULL)
           {
              while (! feof(fichero))
              {                          
                 fread(&persona[i],sizeof(persona), 1, fichero);
                 for (i=0; i<personas; i++)
                 {
                    printf("%d.- %s\n", i+1, persona[i].nombre);
                    j ++;
                 }
                 if (j == 0)
                 {
                    printf("No hay ningun nombre guardado.\n");
                    break;
                 }
                 else
                    if (j == 1) printf("Solo hay un nombre guardado.\n");
                    else
                       printf("Hay %d nombres guardados.\n", personas);
              }
           }
           fclose(fichero);
           break;
               
        case 3:    /* Ver todos los datos de una persona */
           printf("Numero de ficha a leer? ");
           scanf("%d", &numFicha);
           if ((numFicha >0) && (numFicha <= personas))
           {        
              i=numFicha-1;
              fichero = fopen("agenda.dat", "rb");
              if (fichero != NULL)
              {
                 fseek(fichero, sizeof(persona)*(i), SEEK_SET);
                 fread(&persona[i], sizeof(persona), 1,  fichero);
                 fflush(stdin);  
                 printf("%d.- %s %s %s %s %hd %hd %hd\n\n", i+1, persona[i].nombre, persona[i].direccion,
                    persona[i].celular, persona[i].email, persona[i].dia, persona[i].mes, persona[i].anyo);
              }
              fclose(fichero);
           }
           else
              printf("Escriba un numero de ficha valido.");            
           break;
        }
      }
      while (opcion != 0);  
      fichero = fopen("agenda.dat", "ab");    
      for (i=0; i<personas; i++)    
         fwrite(&persona[i], sizeof(persona), 1, fichero);
           
      fclose(fichero);
      return 0;
                           
}

            .....Saludos!





 


09-Aug-2012 16:57
Nacho Cabanes (+83)

Veamos...

Vamos por partes. La primera versión de la agenda está en el apartado 6.4 y dice "Una agenda que maneje los siguientes datos: nombre, dirección, tlf móvil, email, y día, mes y año de nacimiento (estos tres últimos datos deberán ser números enteros cortos). Deberá tener capacidad para 100 fichas. Se deberá poder añadir un dato nuevo, visualizar los nombres de las fichas existentes, o mostrar todos los datos de una persona (se preguntará al usuario cual es el nombre de esa persona que quiere visualizar). Al empezar el programa, leerá los datos de un fichero llamado agenda.dat (si existe). Al terminar, guardará todos los datos en ese fichero."

Como es el primer ejercicio de ese nivel de complejidad, está resuelto en

http://www.nachocabanes.com/c/curso/c_soluciones.php


La segunda versión de la agenda es el ejercicio 6.8a (en la versión 0.90 del curso), que va a pasar a ser el 6.8c (en la 0.95), para que los dos ejercicios anteriores sean más fáciles que éste y el nivel de práctica tenga un incremento de dificultad más progresivo.

Esta versión dice "Mejorar la agenda anterior (del apartado 6.4), para guardar y leer cada ficha (struct) de una vez, usando fwrite/fread y sizeof, como en el último ejemplo."

Se trata de no usar "fprintf" y "fscanf", sino guardar cada struct como un bloque (o incluso todo el array a la vez) usando "fread" y "fwrite".


La tercera versión es la del apartado 6.10, que dice "Mejorar la última versión de la agenda anterior (la que usa fwrite, fread y sizeof, del apartado 6.8) para que no lea todas las fichas a la vez, sino que lea una única ficha del disco cada vez que lo necesite, saltando a la posición en que se encuentra dicha ficha con fseek."

Se trata de que en esta versión no se use ningún array, sino que solo haya una ficha en memoria cada vez. Si se añade un dato, habrá que situarse al final del fichero con "fseek" para guardar allí; si se debe mostrar todos, habrá que ir leyendo cada uno justo antes de mostrarlo.


09-Aug-2012 17:24
Nacho Cabanes (+83)

Atendiendo a eso, echo un vistazo a los fuentes:

- El primero de este hilo, a pesar de estar planteado como solución para el 6.10, usa un array, de modo que se acerca más a la solución del 6.8.

Aun así hay cosas que corregir:


* Como indica Antonio, la variable que se usa de contador en la lectura es incorrecta:

int i, j, numFicha;
...
fread(&persona[i],sizeof(persona), 1, fichero);          
...
personas ++;

Como podéis observar, se guarda cada nueva ficha en la posición "i", que no tiene valor inicial y no se incrementa, y en lugar de eso se incrementa una variable "personas".


* Intenta evitar los "break": aun siendo mucho menos críticos que los "goto", llevan a código difícil de seguir. En vez de hacer

if (feof(fichero)) break;
...
if (feof(fichero)) break;

intenta mejor

if (!feof(fichero))
{
 ...
}


* No entiendo por qué haces dos lecturas en cada pasada del "while". Ya que es un "while", que la siguiente lectura se haga en la siguiente pasada.


* En la opción de añadir, es de esperar que cuando escribes en fichero acabes de leer y estés al final. Pero si más adelante añades alguna opción de "buscar" o similar, quizá no ocurra, así que no cuesta nada asegurarse incluyendo un "fseek" justo antes de guardar.


* En el caso 2, por legibilidad, no llames "j" a la variable auxiliar sino un nombre más descriptivo, como "cantidadDeDatosEncontrados". Piensa que un programa se escribe una vez pero se lee muchas más, especialmente si existe algún fallo. Cuanto más fácil de seguir sea el fuente, más fácil te resultará encontrar los errores o ampliar las funcionalidades.


* En el caso 3, ¿por qué haces "fflush" de la entrada sólo si se ha introducido un número válido?


* A la hora de guardar datos, si usas el modo "ab" cuando guardas todo el array, tendrás 100 fichas tras la primera ejecución, 200 fichas tras la segunda, 300 fichas tras la tercera ejecución y así sucesivamente. El modo de añadir sólo se usa cuando guardas una nueva ficha al final de las existente; si vuelcas todo el array, que contiene tanto los datos antiguos como los nuevos, deberías usar el modo "wb".


Con eso se podrá completar el 6.8c (según la nueva numeración), y, a partir de ése, hacer el 6.10 ficha a ficha.


10-Aug-2012 05:42
Pablo Rampa

Estoy muy agradecido por su extensa respuesta hacia este
ejercicio en particular.
Voy a seguir sus indicaciones y espero mejorar los códigos.
De nuevo, muchas gracias y que siga disfrutando sus cortas vacaciones (si se lo permitimos los miembros del Foro).
Un saludo y
             ...¡adelante!  
                                                     B-)  






(No se puede continuar esta discusión porque tiene más de dos meses de antigüedad. Si tienes dudas parecidas, abre un nuevo hilo.)