Escritura y lectura de archivos binarios

Iniciado por ++c, 3 Septiembre 2014, 19:04 PM

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

++c

Hola amigos, estoy tratando de hacer un código de archivo binario con array de estructuras pero no encuentro como hallar adecuadamente la lectura de los registros que introduzco con fread.

Si introduzco dos personas como en el ejemplo necesita de tres ocasiones para leerlas hasta llegar a final de archivo. No encuentro la manera sencilla de que haga las lecturas correctas de las personas que solicito a través de un número.

Dejo el código para conocer en que puedo estar fallando, gracias!!



#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 2

struct{
    char nombre[20];
    char ciudad[20];
    int edad;
}persona[N];

int main(void)
{
    char temporal[20];
    char name[15]="gente.dat";
    int i,numero,leo;
    FILE *fichero;
    if((fichero=fopen(name,"ab"))!=NULL){
        do{
            for(i=0;i<N;i++){
                printf("\nIntroduzca nombre: ");
                gets(persona[i].nombre);
                if(strcmp(persona[i].nombre,"")!=0){
                    printf("\nIntroduzca ciudad: ");
                    gets(persona[i].ciudad);
                    printf("\nIntroduzca edad: ");
                    fgets(temporal,19,stdin);
                    sscanf(temporal,"%d",&persona[i].edad);
                    fwrite(&persona[i],sizeof(persona),1,fichero);
                }
            }
        }while(strcmp(persona[i].nombre,"")!=0);
        fclose(fichero);
    }
    else{
        printf("\nError en apertura fichero");
        exit(1);
    }
    if((fichero=fopen(name,"rb"))!=NULL){
        do{
                printf("\nIntroduzca numero de persona a visualizar: ");
                fgets(temporal,19,stdin);
                sscanf(temporal,"%d",&numero);
                for(i=0;i<N;i++){
                     if(numero==i){
                        printf("\nNombre: %s",persona[i].nombre);
                        printf("\nCiudad: %s\n",persona[i].ciudad);
                        printf("Edad: %d",persona[i].edad);
                        leo=fread(&persona[i],1,sizeof(persona),fichero);
                    }
                }
        }while(leo!=0);
        fclose(fichero);
    }else{
        printf("\nError en apertura fichero");
        exit(1);
    }
    return 0;
}


eferion


if((fichero=fopen(name,"rb"))!=NULL){
  do{
    printf("\nIntroduzca numero de persona a visualizar: ");
    // ...

    for(i=0;i<N;i++){
      if(numero==i){
        printf("\nNombre: %s",persona[i].nombre);
        printf("\nCiudad: %s\n",persona[i].ciudad);
        printf("Edad: %d",persona[i].edad);
        leo=fread(&persona[i],1,sizeof(persona),fichero);
      }
    }
  }while(leo!=0);
  fclose(fichero);
}


No me ha quedado muy clara esta parte del código.

Intentas leer un registro determinado del archivo... pero el mecanismo es extraño. Me explico:

1. Abres el archivo
2. Preguntas al usuario el índice del registro a visualizar??? antes de haber leído el archivo ?????
3. Después, localizas ese registro, lo sacas por pantalla y entonces sobreescribes su contenido con el primer registro que te encuentras en el fichero. Al final lo que acabas de leer te lo guardas para ti y no lo sacas por pantalla.
4. Vuelves al paso 2 hasta que hayas leído todos los registros del archivo.

El código de arriba lo puedes simplificar. Quizás te ayude a ver que no tiene mucho sentido :


if((fichero=fopen(name,"rb"))!=NULL){
  do{
    printf("\nIntroduzca numero de persona a visualizar: ");
    // ...

    printf("\nNombre: %s",persona[numero].nombre);
    printf("\nCiudad: %s\n",persona[numero].ciudad);
    printf("Edad: %d",persona[numero].edad);
    leo=fread(&persona[numero],1,sizeof(persona),fichero);
  }while(leo!=0);
  fclose(fichero);
}


El comportamiento es francamente extraño.

Quizás deberías comentar qué se espera acerca del funcionamiento de este programa.

rir3760

Cita de: ++c en  3 Septiembre 2014, 19:04 PMestoy tratando de hacer un código de archivo binario con array de estructuras pero no encuentro como hallar adecuadamente la lectura de los registros que introduzco con fread.
Como ya te comento eferion el programa tiene bastantes errores, si no tienes un curso o libro de calidad debes conseguir uno, recomendaciones sobre ellos las puedes revisar mediante el motor de búsqueda de los foros.

Diría que el error principal es lógico (imprimir el registro y solo después de ello leerlo) pero debido a la cantidad de errores es mejor rescribir esa parte desde cero. Los pasos son:

1) Pides el numero de registros al usuario.
2) Llamas a fread tratando de leer el numero indicado (pueden ser menos).
3) Utilizas un bucle para imprimir cada registro leído.

Mas o menos así:
if ((fichero = fopen(name, "rb")) == NULL){
   /* Manejo de error */
}else {
   char aux[128];
   int regs_a_leer;
   int regs_leidos;
   
   /* 1) Obtener el numero de registros a leer */
   puts("Numero de personas a visualizar:");
   fgets(aux, sizeof aux, stdin);
   sscanf(aux, "%d", &regs_a_leer);
   
   /* 2) Tratar de leer el numero indicado */
   regs_leidos = fread(persona, sizeof persona[0], regs_a_leer, fichero);
   
   /* 3) Imprimir los registros leidos */
   for (i = 0; i < regs_leidos; i++){
      printf("Registro No %d\n", i + 1);
     
      printf("Nombre: %s\n", persona[i].nombre);
      printf("Ciudad: %s\n", persona[i].ciudad);
      printf("Edad: %d\n", persona[i].edad);
   }
   
   fclose(fichero);
}


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

++c

#3
Hola!!

Gracias por los consejos eferion y rir3760.

Me han ayudado vuestros consejos y metodología para trabajar con ficheros.

No me he explicado muy bien y espero expresarme lo que pretendo hacer.

Mirar lo que quiero que haga el programa en la lectura:

1. Lea el fichero.

2. Que pregunte al usuario por el nº de registro que desea visualizar. Ejemplo:
    Se han introducido y guardado previamente en la primera parte del código con        fwrite:
    - Nombre= José , se habrá guardado en persona[0]
    - Nombre2= David , persona[1]

3. El programa mostrará los datos correspondientes a ese usuario.

He intentado hacerlo de otra forma pero solo consigo que me lea ambos contactos(José y David) si introduzco el contacto '0', si coloco el que me busque el contacto '1' me muestra por pantalla en blanco las opciones que deseo se me visualicen.


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 2

struct{
    char nombre[20];
    char ciudad[20];
    int edad;
}persona[N];

int main(void)
{
    char temporal[20];
    char name[15]="gente.dat";
    int i,numero;
    FILE *fichero;
    if((fichero=fopen(name,"wb"))!=NULL){
        do{
            for(i=0;i<N;i++){
                printf("\nIntroduzca nombre: ");
                fgets(persona[i].nombre,sizeof(persona),stdin);
                if(strcmp(persona[i].nombre,"")!=0){
                    printf("\nIntroduzca ciudad: ");
                    fgets(persona[i].ciudad,sizeof(persona),stdin);
                    printf("\nIntroduzca edad: ");
                    fgets(temporal,19,stdin);
                    sscanf(temporal,"%d",&persona[i].edad);
                    fwrite(&persona[i],sizeof(persona),1,fichero);
                }
            }
        }while(strcmp(persona[i].nombre,"")!=0);
        fclose(fichero);
    }
    else{
        printf("\nError en apertura fichero");
        exit(1);
    }
    if((fichero=fopen(name,"rb"))!=NULL){
        fread(&persona,sizeof(persona),1,fichero); /*leo el archivo antes de preguntar*/
        while(!feof(fichero)){
                printf("\nIntroduzca numero de persona a visualizar: ");
                fgets(temporal,19,stdin);
                sscanf(temporal,"%d",&numero);
                printf("\nNombre: %s",persona[numero].nombre);
                printf("\nCiudad: %s\n",persona[numero].ciudad);
                printf("Edad: %d",persona[numero].edad);
                fread(&persona[numero],sizeof(persona),1,fichero);
        }
        fclose(fichero);
    }else{
        printf("\nError en apertura fichero");
        exit(1);
    }
    return 0;
}


Pdata: se que no se ve de muy buena forma el uso de !feof por algunos programadores...

Pdata eferion: me sirvió como simplificaste el código.

Pdata rir3760: me guardo tu código y me quedo con la copla de como leer y mostrar contactos.

Saludos!!

rir3760

Cita de: ++c en  4 Septiembre 2014, 23:08 PMMirar lo que quiero que haga el programa en la lectura:

1. Lea el fichero.
Ya que en el programa declaras un array de N elementos solo tienes que utilizar la función fread para tratar de leer ese numero. Ya que se puede leer un numero menor almacenas el valor de retorno de la función en una variable (es el numero de elementos leídos). Eso ya lo explique en mi mensaje anterior:
/* 2) Tratar de leer el numero indicado */
regs_leidos = fread(persona, sizeof persona[0], regs_a_leer, fichero);


Cita de: ++c en  4 Septiembre 2014, 23:08 PM2. Que pregunte al usuario por el nº de registro que desea visualizar.
Solo tienes que utilizar la función scanf para obtener el indice del elemento en el array: 0 .. regs_leidos - 1.

Cita de: ++c en  4 Septiembre 2014, 23:08 PM3. El programa mostrará los datos correspondientes a ese usuario.
Imprimes el valor de los campos del elemento en cuestión, de nuevo eso ya lo tienes.

Cita de: ++c en  4 Septiembre 2014, 23:08 PMPdata: se que no se ve de muy buena forma el uso de !feof por algunos programadores.
El problema con feof es que usualmente es innecesaria ya que se puede utilizar directamente el valor de retorno de la función utilizada.

Por ejemplo en tu programa se debe duplicar la sentencia de lectura para que el bucle funcione correctamente (con feof) cuando utilizando el valor de retorno de fread te olvidas de feof y la duplicación:
while (fread(&persona,sizeof(persona),1,fichero) == 1){
   printf("\nIntroduzca numero de persona a visualizar: ");
   fgets(temporal,19,stdin);
   sscanf(temporal,"%d",&numero);
   
   printf("\nNombre: %s",persona[numero].nombre);
   printf("\nCiudad: %s\n",persona[numero].ciudad);
   printf("Edad: %d",persona[numero].edad);
}


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

++c

Hola rir3760,

gracias por las aclaraciones, sólo me queda una duda y es la que me indicas aquí:

Citar
Solo tienes que utilizar la función scanf para obtener el indice del elemento en el array: 0 .. regs_leidos - 1.

La duda me viene en que no deduzco como recorrer ese array con regs_leidos...

Saludos!!

rir3760

Tal vez no me estoy explicando correctamente ya que no hay necesidad alguna de "recorrer" el array, simplemente se imprime el registro indicado cuyo indice obtienes con la función scanf.

Solo para estar seguro.

1) Al utilizar fread en esta forma:
fread(persona, sizeof persona[0], regs_a_leer, fichero);
Se le indica que lea el numero de registros indicado por "regs_a_leer", el objetivo de ello es leer todos los registros en el archivo y como máximo (el tercer argumento de la función) debes indicar la capacidad del array "persona". De nuevo se leen todos los registros.

2) La función puede leer menos bien porque se genere un error o porque simplemente no hay tantos registros en el archivo. Por eso la función retorna el numero de elementos leídos y almacenamos ese valor en la variable "regs_leidos":
regs_leidos = fread(persona, sizeof persona[0], regs_a_leer, fichero);

3) Si todo sale bien el valor de "regs_leidos" sera igual a "regs_a_leer" (si no es así hay un error tal vez lógico, tal vez en la lectura del archivo). Solo resta obtener el indice del registro e imprimirlo:
int i;

/* ... */

puts("Indice del registro a imprimir:");
scanf("%d", &i);

/* ... */

/* Se imprimen los campos del registro "persona[i]" */


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

++c

Hola rir3760,

gracias de nuevo por las explicaciones, me han servido para resolver los algoritmos de dos formas, te los agrego para que veas como han quedado:


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 2

struct{
    char nombre[20];
    char ciudad[20];
    int edad;
}persona[N];

int main(void)
{
    char temporal[20];
    char name[15]="gente.dat";
    int i,numero;
    FILE *fichero;
    if((fichero=fopen(name,"wb"))!=NULL){
        do{
            for(i=0;i<N;i++){
                printf("\nIntroduzca nombre: ");
                fgets(persona[i].nombre,sizeof(persona),stdin);
                if(strcmp(persona[i].nombre,"")!=0){
                    printf("\nIntroduzca ciudad: ");
                    fgets(persona[i].ciudad,sizeof(persona),stdin);
                    printf("\nIntroduzca edad: ");
                    fgets(temporal,19,stdin);
                    sscanf(temporal,"%d",&persona[i].edad);
                    fwrite(&persona[i],sizeof(persona[i]),1,fichero);
                }
            }
        }while(strcmp(persona[i].nombre,"")!=0);
        fclose(fichero);
    }
    else{
        printf("\nError en apertura fichero");
        exit(1);
    }
    if((fichero=fopen(name,"rb"))!=NULL){

       while (fread(&persona,sizeof(persona),N,fichero) !=NULL){
           printf("\nIntroduzca numero de persona a visualizar: ");
           scanf("%d",&numero);
           printf("\nNombre: %s",persona[numero].nombre);
           printf("\nCiudad: %s\n",persona[numero].ciudad);
           printf("Edad: %d",persona[numero].edad);
        }
        fclose(fichero);
    }else{
        printf("\nError en apertura fichero");
        exit(1);
    }
    return 0;
}


Y este el otro sin uso de array de struct:


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

struct{
    char nombre[20];
    char ciudad[20];
    int edad;
}persona;

int main(void)
{
    char temporal[20];
    char name[15]="gente.dat";
    int i,numero;
    FILE *fichero;
    if((fichero=fopen(name,"wb"))!=NULL){
        do{
                printf("\nIntroduzca nombre: ");
                fgets(persona.nombre,sizeof(persona.nombre),stdin);
                if(strcmp(persona.nombre,"\n")!=0){
                    printf("\nIntroduzca ciudad: ");
                    fgets(persona.ciudad,sizeof(persona.ciudad),stdin);
                    printf("\nIntroduzca edad: ");
                    fgets(temporal,19,stdin);
                    sscanf(temporal,"%d",&persona.edad);
                    fwrite(&persona,sizeof(persona),1,fichero);
                }

        }while(strcmp(persona.nombre,"\n")!=0);
        fclose(fichero);
    }
    else{
        printf("\nError en apertura fichero");
        exit(1);
    }
    if((fichero=fopen(name,"rb"))!=NULL){

        printf("\nIntroduzca numero de persona a visualizar: ");
        scanf("%d",&numero);

        i=0;
        while ( (fread(&persona,sizeof(persona),1,fichero) !=NULL) && (i<numero) ){
           ++i;
        }
        if (!feof(fichero))
        {
           printf("\nNombre: %s",persona.nombre);
           printf("\nCiudad: %s\n",persona.ciudad);
           printf("Edad: %d",persona.edad);
        }
        else
            printf ("La persona no existe.\n");
        fclose(fichero);
    }else{
        printf("\nError en apertura fichero");
        exit(1);
    }
    return 0;
}


Seguro que encuentras algún pero... pero funcionan, jeje

Saludos y gracias