Leer registro encabezado de archivo | C

Iniciado por milx86, 19 Octubre 2014, 06:54 AM

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

milx86

Buenas a todos.
Les cuento, tengo un archivo que almacena registros de longitud fija.

Mi archivo tiene un registro encabezado que almacena info sobre:
* El número de registros de datos del archivo
* La longitud de los registros de datos

+----------------------+
+ 5 + recordLength    +
+----------------------+
+ NR+ MAS CAMPOS  +
+----------------------+
+ 1 + ....   data  ........+
+ 2 + ....   data  ........+
+ 3 + ....   data  ........+
+ 4 + ....   data  ........+
+ 5 + ....   data  ........+

typedef struct Record{
    char firstName[20];
    char lastName[20];
    char nationality[20];
    char DNI[9];
    int age;
    int recordNumber;
}Record;

typedef struct Header{
    int numberOfRecords;
    int recordLength;
}Header;

Lo que hago al crear un archivo es inicializar los campos del registro encabezado y grabarlo:
void createFile(char* nameFile)
{
    FILE* fd;
    Header buffer;
    buffer.numberOfRecords = 0;
    buffer.recordLength = sizeof(Record);
    fd = fopen(nameFile,"wt");
    fseek(fd,0,0);
    fwrite(&buffer,1,sizeof(Header),fd);
    // Compruebo el valor inicial que se grabo en numberOfRecords y me da 0, es correcto
    printf("Valor: %d",buffer.numberOfRecords);
    fclose(fd);
}


Como se habrán dado cuenta cada registro Record tiene un campo el cual indica que  numero de registro es.
    int recordNumber;
Lo que hago para asignarle el valor al campo recordNumber, leo el encabezado(el numero de registros) y a ese le sumo uno, El cual seria el numero de registros actuales.
El problema es en la función para agregar un registro al archivo:
void addRecord(FILE** fd,Record data)
{
    struct Header buffer;
    fread(&buffer, 1,sizeof(Header) , *fd);
    buffer.numberOfRecords++;

    printf("Valor?: %d\n",buffer.numberOfRecords);

    data.recordNumber = buffer.numberOfRecords;
    // Compruebo el contenido del registro recibido.
    // Pero el campo NR no se actualizo... Sale un numero extrano
    printf("% 10d% 12s% 12s% 12s% 12s% 12d\n",
           data.recordNumber,
           data.firstName,
           data.lastName,
           data.nationality,
           data.DNI,
           data.age
          );
    printf("\n\t\t\t    Press enter key to continue");
    fgetchar();
//
//    int pos = (data.recordNumber-1) * buffer.recordLength + sizeof(Header);
//    fseek(*fd,2,0);
//    fwrite(&data,1, buffer.recordLength, *fd);
//    fseek(*fd,0,0);
//    fwrite(&buffer, 1,sizeof(Header), *fd);
}


Mi problema es que cuando creo el archivo e inicializo el encabezado (numero de registros igual 0) funciona perfecto, pero cuando voy a leer luego el encabezado en ese campo esta un valor "extraño".

Aqui les dejo mi programa:
#ifdef __linux__
#define CLEAN "CLEAR"
#endif

#ifdef __MINGW32__
#define CLEAN "CLS"
#endif

#ifdef __MINGW64__
#define CLEAN "CLS"
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define TAM_CADENA 20

typedef struct Record
{
    char firstName[20];
    char lastName[20];
    char nationality[20];
    char DNI[9];
    int age;
    int recordNumber;
}Record;

typedef struct Header
{
    int numberOfRecords;
    int recordLength;
}Header;

void readLine(char cadena[], int tam);
int readInt();
void addRecord(FILE** fd,Record data);
int getRecordNumber(FILE** fd);
void createFile(char* nameFile);
int subMenu(char* fileName);
void menu();

int main(int argc, char *argv[])
{
    menu();
    return 0;
}

void menu()
{
    int op = 0;
    char nameFile[TAM_CADENA];
    do{
        system(CLEAN);
        printf("\n\t\t# | > _ _ _ _ _ _ __ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ < | #");
        printf("\n\t\t# | >                                                < | #");
        printf("\n\t\t# | >              [x1] Create File                  < | #");
        printf("\n\t\t# | >              [x2] Open file for...             < | #");
        printf("\n\t\t# | >              [x3] Dump File                    < | #");
        printf("\n\t\t# | >              [x4] Search Record                < | #");
        printf("\n\t\t# | >              [x5] Filter by Nationality        < | #");
        printf("\n\t\t# | >              [x6] Exit                         < | #");
        printf("\n\t\t# | >_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ < | #");
        printf("\n\t\t##                                                      ##");
        printf("\n\t\t\t        Enter option: ");
        op = readInt();
        switch(op){
            case 1: printf("\n\t\t\t    Enter the file name: ");
                    readLine(nameFile,TAM_CADENA);
                    createFile(nameFile);
                    printf("\n\t\t\t    Press enter key to continue");
                    fgetchar();
                    break;

            case 2: printf("\n\t\t\t    Enter the file name: ");
                    readLine(nameFile,TAM_CADENA);
                    subMenu(nameFile);
                    break;

            case 3: printf("\n\t\t\t    Enter the file name: ");
                    readLine(nameFile,TAM_CADENA);
                    seeAll(nameFile);
                    printf("\n\t\t\t    Press enter key to continue");
                    fgetchar();
                    break;

            case 4: printf("\n\t\t\t    Press enter key to continue");
                    fgetchar();
                    break;

            case 5: printf("\n\t\t\t    Press enter key to continue");
                    fgetchar();
                    break;

            case 6: printf("\n\t\t\t    Good bye :)");
                    printf("\n\t\t\t    Press enter key to continue");
                    fgetchar();
                    break;

            default:printf("\n\t\t\t    [msgError]: Invalid option.");
        }
    }while(op!=6);
}

int subMenu(char* fileName)
{
    FILE* fd;
    if( (fd=fopen(fileName,"a+t")) == NULL )
    {
        printf("\nCould not open the file.");
        return -1;
    }
    else
    {
        int op = 0, R;
        Record buffer;
        do{
            system(CLEAN);
            R = getRecordNumber(&fd);
            printf("\n\t\t##                                                      ##");
            printf("\n\t\t# | >>_ _ _++=[-> File: %s   R: %d<-]=++_ _ _ << | #", fileName,R);
            printf("\n\t\t# | >_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ < | #");
            printf("\n\t\t# | >                                                < | #");
            printf("\n\t\t# | >              [x1] Add new records.             < | #");
            printf("\n\t\t# | >              [x2] Change record                < | #");
            printf("\n\t\t# | >              [x3] Close file                   < | #");
            printf("\n\t\t# | >_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ < | #");
            printf("\n\t\t##                                                      ##");
            printf("\n\t\t\t        Enter option: ");
            op = readInt();
            switch(op){
                case 1: printf("\n\t\t\t    Enter First Name: ");
                        readLine(&buffer.firstName,TAM_CADENA);
                        printf("\n\t\t\t    Enter Last Name: ");
                        readLine(buffer.lastName,TAM_CADENA);
                        printf("\n\t\t\t    Enter Nationality: ");
                        readLine(buffer.nationality,TAM_CADENA);
                        printf("\n\t\t\t    Enter DNI: ");
                        readLine(buffer.DNI,9);
                        printf("\n\t\t\t    Enter Age: ");
                        buffer.age = readInt();
                        addRecord(&fd, buffer);
                        break;

                case 2: printf("\n\t\t\t    Press enter key to continue");
                        fgetchar();
                        break;

                case 3: fclose(fd);
                        printf("\n\t\t\t    File closed.");
                        break;

                default:printf("\n\t\t\t    [msgError]: Invalid option.");
            }
        }while(op!=3);
    }
    return 0;
}

void createFile(char* nameFile)
{
    FILE* fd;
    struct Header buffer;
    buffer.numberOfRecords = 0;
    buffer.recordLength = sizeof(Record);
    fd = fopen(nameFile,"wt");
    fseek(fd,0,0);
    fwrite(&buffer,1,sizeof(Header),fd);
    // Compruebo el valor inicial que se grabo en numberOfRecords
    // 0 es correcto
    printf("Valor de nr: %d",buffer.numberOfRecords);
    fclose(fd);
}


int getRecordNumber(FILE** fd)
{
    struct Header buffer;
    fread(&buffer, 1, sizeof(Header),*fd);
    return buffer.numberOfRecords;
}


void addRecord(FILE** fd,Record data)
{
    struct Header buffer;
    fread(&buffer, 1,sizeof(Header) , *fd);
    buffer.numberOfRecords++;

    printf("Valor de nr: %d\n",buffer.numberOfRecords);

    data.recordNumber = buffer.numberOfRecords;
    // Compruebo el contenido del registro recibido.
    // Pero el campo NR no se actualizo... Sale un numero extraño
    printf("% 10d% 12s% 12s% 12s% 12s% 12d\n",
           data.recordNumber,
           data.firstName,
           data.lastName,
           data.nationality,
           data.DNI,
           data.age
          );
    printf("\n\t\t\t    Press enter key to continue");
    fgetchar();
//
//    int pos = (data.recordNumber-1) * buffer.recordLength + sizeof(Header);
//    fseek(*fd,2,0);
//    fwrite(&data,1, buffer.recordLength, *fd);
//    fseek(*fd,0,0);
//    fwrite(&buffer, 1,sizeof(Header), *fd);
}

int seeAll(char* nameFile)
{
    FILE* fd;
    if( (fd=fopen(nameFile,"rt")) == NULL )
    {
        printf("\nCould not open the file.");
        return -1;
    }

    else
    {
        struct Record buffer;
        struct Header header;
        fread(&header,1,sizeof(Header), fd);
        while(fread(&buffer,1,header.recordLength,fd) == 1)
        {
            printf("% 10d% 12s% 12s% 12s% 12s% 12d\n",
                   buffer.recordNumber,
                   buffer.firstName,
                   buffer.lastName,
                   buffer.nationality,
                   buffer.DNI,
                   buffer.age
                  );
        }

        fclose(fd);
    }
    return 0;
}

int readInt()
{
  char buf[BUFSIZ];
  char *p;
  long int i;

  if (fgets(buf, sizeof(buf), stdin) != NULL)
  {
    i = strtol(buf, &p, 10);
    if (buf[0] != '\n' && (*p == '\n' || *p == '\0'))
      return i;
  }
}

void readLine(char cadena[], int tam)
{
    char c;
    fgets(cadena,tam,stdin);
    if (cadena[strlen(cadena)-1] == '\n')
        cadena[strlen(cadena)-1] = '\0';
    else
    {
        cadena[tam-1] = '\0';
        while((c = getchar()) != '\n'){};
    }
}


Gracias de antemano. :)
La abstracción es la clave para lidiar con la complejidad.

avesudra

Ayer lo estuve mirando y es rarísimo, seguro que se escapa algo, pero no lo veo. Vale, justo cuando estaba escribiendo esto creo que me he dado cuenta del error, tú abres el archivo para escribirlo al desde el final no para leerlo(voy a mirar a ver si es eso)
Regístrate en

rir3760

Cita de: freeCode en 19 Octubre 2014, 06:54 AMMi problema es que cuando creo el archivo e inicializo el encabezado (numero de registros igual 0) funciona perfecto, pero cuando voy a leer luego el encabezado en ese campo esta un valor "extraño".
El problema principal se debe a que en la función "subMenu" abres el archivo en modo "a+t" y con ello todas las operaciones de escritura se realizaran al final del archivo sin importar el uso de funciones de posicionamiento como fseek y rewind. En su lugar debes abrir el archivo en el modo "r+b" cuidando que antes de cualquier operación de escritura te posiciones al principio o final del archivo (dependiendo si vas a escribir el encabezado o agregar un registro) mediante la función fseek.

Ademas se debe cambiar/corregir una parte significativa del programa:

* Si declaras una macro no debes darle un nombre que inicie con un guion bajo ya que esos nombres se reservan para la implementación (algunos ejemplos son __DATE__, __FILE__, __LINE__, etc.).

* Cuando se declara una función con paréntesis vacíos:
int readInt();

/* ... */

void menu();

Ello indica un numero no determinado de argumentos, para indicar que no recibe argumentos debes utilizar "(void)".

* Si no utilizas los argumentos pasados mediante la linea de comandos cambia la definición de la función principal a "int main(void) ..." o "int main() ...".

* La biblioteca estándar de C no incluye una función con el nombre "fgetchar", en su lugar utiliza getchar o mejor todavía elimina por completo esa llamada.

* En el bloque de prototipos falta la declaración de la función "seeAll".

* En la función "subMenu" en la llamada a "readLine":
readLine(&buffer.firstName, TAM_CADENA);
El operador "dirección de" (el '&') esta de mas en su primer argumento, eliminalo.

* En la función "readInt" no se especifica el valor a retornar si ocurre un error y si la linea es demasiado larga el restante se quedara en el bufer de la entrada estándar (para el caso mejor utiliza scanf para leer el entero y a continuación descarta el resto de la linea con un bucle.

* En la función "readLine" si el ultimo carácter no es el avance de linea no es necesario sobrescribir este con un '\0' porque fgets lo agrega automáticamente. Y el tipo de retorno de getchar es int, la función hay que cambiarla a:
void readLine (char cadena[], int tam)
{
   if (fgets(cadena, tam, stdin) != NULL){
      size_t i = strlen(cadena) - 1;
     
      if (cadena[i] == '\n')
         cadena[i] = '\0';
      else {
         int ch;
         
         while ((ch = getchar()) != EOF && ch != '\n')
            ;
      }
   }
}


* En la funcion "seeAll" el bucle para imprimir todos los registros:
while (fread (&buffer, 1, header.recordLength, fd) == 1){
   /* ... */
}

No funcionara correctamente, las razones de ello te las explique en tu tema Funcion que no puede retornar un struct [C].

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

milx86

Gracias a ambos por su ayuda.
En la universidad enseñan una mezcla de C/C++ que me incomoda, no creo que cuando termine acepten desarrollar proyectos de esa manera  :¬¬ , por lo que estoy aprendiendo C por mi cuenta con Programación en C | Joyanes, pero bueno sin salir del tema..

Tambien me di cuenta de otro error.
En mi función submenu estoy trabajando todo con el mismo fd y al inicio del submenu ya leo el encabezado(cuando muestro el nombre del archivo y el numero de registros.)
Y en la funcion para agregar vuelvo a leer el encabezado,pero el "cursor" esta posicionado despues del registro de encabezado , tengo que retroceder al inicio xD!

Estoy haciendo los cambios que me dijeron con el codigo que publique aqui y ya me funciona, cuando llegue a casa publicaré mi programa completo, espero tambien le sirva a alguien cuando visite el hilo  :).

Y por curiosidad(y sin ser chupamedias), quizá es por la experiencia que conoces tanto,como es que funciona el lenguaje y sus funciones, eso casi no e encontrado (o no e sabido buscar), en su mayoria dicen "que es lo que hace", pero no "como lo hace".
Me recomendarías alguna documentación de nivel básico, quiero conocer más a C.

Saludos
La abstracción es la clave para lidiar con la complejidad.

avesudra

#4
Cita de: freeCode en 22 Octubre 2014, 17:09 PM
Gracias a ambos por su ayuda.
En la universidad enseñan una mezcla de C/C++ que me incomoda, no creo que cuando termine acepten desarrollar proyectos de esa manera  :¬¬ , por lo que estoy aprendiendo C por mi cuenta con Programación en C | Joyanes, pero bueno sin salir del tema..

Tambien me di cuenta de otro error.
En mi función submenu estoy trabajando todo con el mismo fd y al inicio del submenu ya leo el encabezado(cuando muestro el nombre del archivo y el numero de registros.)
Y en la funcion para agregar vuelvo a leer el encabezado,pero el "cursor" esta posicionado despues del registro de encabezado , tengo que retroceder al inicio xD!

Estoy haciendo los cambios que me dijeron con el codigo que publique aqui y ya me funciona, cuando llegue a casa publicaré mi programa completo, espero tambien le sirva a alguien cuando visite el hilo  :).

Y por curiosidad(y sin ser chupamedias), quizá es por la experiencia que conoces tanto,como es que funciona el lenguaje y sus funciones, eso casi no e encontrado (o no e sabido buscar), en su mayoria dicen "que es lo que hace", pero no "como lo hace".
Me recomendarías alguna documentación de nivel básico, quiero conocer más a C.

Saludos
En cuanto a lo último el "cómo lo hace" no es parte del desarrollador de otras aplicaciones pues no hay que reinventar la rueda, pero entiendo tu curiosidad pues yo también la tuve y la sigo teniendo, no tienes más que descargar la glibc y leer el código ;)

http://www.gnu.org/software/libc/

http://ftp.gnu.org/gnu/glibc/ De aquí puedes descargarte el código de todas las funciones que usas de C, como están hechas y tal, no creo que te enteres de mucho al igual que yo no lo hice en su momento, es una biblioteca demasiado grande, pero si puedes echarle un ojo a las funciones matemáticas que eso es curioso.
Saludos
Regístrate en