Foros del Web » Programación para mayores de 30 ;) » C/C++ »

problema leyendo un archivo con estructuras

Estas en el tema de problema leyendo un archivo con estructuras en el foro de C/C++ en Foros del Web. Hola, estoy intentando hacer un programa para controlar gastos, y tengo varios problemas. El primero es, que pese a utilizar una lista enlazada para guardar ...
  #1 (permalink)  
Antiguo 17/01/2008, 14:17
 
Fecha de Ingreso: enero-2008
Mensajes: 5
Antigüedad: 16 años, 4 meses
Puntos: 0
problema leyendo un archivo con estructuras

Hola,

estoy intentando hacer un programa para controlar gastos, y tengo varios problemas. El primero es, que pese a utilizar una lista enlazada para guardar los datos, me toca reservar memoria para los elementos que vaya a utilizar. Si no, no funciona. Esto no puede ser así, me extraña una barbaridad...

Lo segundo es que la funcion abrir(), abre el archivo donde tengo guardados los registros pero no los muestra correctamente. Es decir, yo tengo 4 registros, y normalmente sol ome muestra uno, el primero. A veces, y sin cambair nada, muestra los 4. No entiendo ese comportamiento...

Bueno, ahi bajo dejo el codigo...si alguien me pudiera dar alguna pequeña pista, le estaría tremendamente agradecido...

Código:
#include <stdio.h>
#define MAX_N 10 /* MAX_NOMBRE */
#define MAX_C 30 /* MAX_CONCEPTO */

typedef struct {
    int dia;
    int mes;
    int ano;
} _fecha;


struct gasto{
    int i;
    char nombre[MAX_N];
    char concepto[MAX_C];
    _fecha fecha;
    float importe;
    struct gasto * sigGasto;
};
typedef struct gasto _gasto;

_gasto *DB;

void abrir();
void guardar(_gasto *);

int main(char argv[], int argc) {

    DB = (_gasto *) malloc(sizeof(_gasto));
    DB->sigGasto = (_gasto *) malloc(sizeof(_gasto));
    DB->sigGasto->sigGasto = (_gasto *) malloc(sizeof(_gasto));
    DB->sigGasto->sigGasto->sigGasto = (_gasto *) malloc(sizeof(_gasto));

/*  Para guardar los 4 registros


    _gasto *aux;



    _fecha fecha1 = {29, 03, 1989};
    _fecha fecha2 = {30, 03, 1989};
    _fecha fecha3 = {31, 03, 1989};
    _fecha fecha4 = {01, 04, 1989};


    DB->i = 0;
    strcpy(DB->nombre, "Nombre 1");
    strcpy(DB->concepto, "Concepto 1");
    DB->fecha = fecha1;
    DB->importe = 111.0;
    aux = DB; //para no perder el principio de la lista
    DB = DB->sigGasto;

    DB->i = 1;
    strcpy(DB->nombre, "Nombre 2");
    strcpy(DB->concepto, "Concepto 2");
    DB->fecha = fecha2;
    DB->importe = 222.0;
    DB = DB->sigGasto;

    DB->i = 2;
    strcpy(DB->nombre, "Nombre 3");
    strcpy(DB->concepto, "Concepto 3");
    DB->fecha = fecha3;
    DB->importe = 333.0;
    DB = DB->sigGasto;

    DB->i = 3;
    strcpy(DB->nombre, "Nombre 4");
    strcpy(DB->concepto, "Concepto 4");
    DB->fecha = fecha4;
    DB->importe = 444.0;

    DB->sigGasto = NULL;
*/

    // A VECES ME LOS LEE TODOS Y A VECES SOLO ME PONE ESTO:
    //  |   0 |   Nombre 1 |                     Concepto 1 | 29/ 3/1989 |     0 |
    abrir();
    return 0;
}


// Cada ejecucion da un resultado.
void abrir() {

    FILE * fp;
    fp = fopen("gastos.dts", "rb");
    _gasto *aux;
    aux = (_gasto *) malloc(sizeof(_gasto));

    while (!feof(fp)) {
        fread(aux, sizeof(_gasto), 1, fp);
        printf(" | %3d | %10s | %30s | %2i/%2i/%4i | %5i |\n",
                aux->i, aux->nombre, aux->concepto, aux->fecha.dia,
                aux->fecha.mes, aux->fecha.ano, aux->importe);
        aux = aux->sigGasto;
    }

    fclose (fp) ;
}


void guardar(_gasto *guardar) {

    FILE * fp;
    int contador = 0;

    /* Abro el archivo gastos.dts para escritura */
    fp = fopen("gastos.dts", "wb");

    /* Mientras que haya algun registro "gasto" lo escribe en el archivo gastos.dts */
    while (guardar != NULL) {
        fwrite(guardar, sizeof(_gasto), 1, fp);
        guardar = guardar->sigGasto;
        contador++;
    }

    /* Cierra el arhivo */
    fclose(fp);

    printf("Datos guardados: %i", contador);
}
Gracias por adelantado,
saludos.
  #2 (permalink)  
Antiguo 17/01/2008, 15:15
Avatar de _Lucifer_  
Fecha de Ingreso: junio-2006
Mensajes: 1.662
Antigüedad: 17 años, 11 meses
Puntos: 28
Re: problema leyendo un archivo con estructuras

Bueno, tienes un par de errores de lógica por lo que veo a primera vista.
Código:
...
void abrir() {

    FILE * fp;
    fp = fopen("gastos.dts", "rb");
    _gasto *aux;
    aux = (_gasto *) malloc(sizeof(_gasto));

    while (!feof(fp)) {
        fread(aux, sizeof(_gasto), 1, fp);
        printf(" | %3d | %10s | %30s | %2i/%2i/%4i | %5i |\n",
                aux->i, aux->nombre, aux->concepto, aux->fecha.dia,
                aux->fecha.mes, aux->fecha.ano, aux->importe);
        aux = aux->sigGasto;
    }

    fclose (fp) ;
}
...
En la linea que te marqué, ¿quién es siguiente? Se que es el siguiente elemento de la lista, pero no has reservado memoria para el, así que quien sabe donde te esté guardando los datos, deberías reservar memoria para el siguiente nodo antes de usarlo.

Otra cosa es que no estás liberando la memoria que usar al final de tu programa, eso crea lo que se llama memory leaks, asegurate de hacer un free de todo lo que reservaste al final de tu programa o de cada función dependiendo de lo que hiciste.

Saludos
__________________
Si crees que no tiene sentido, etonces probablemente lo tenga... :arriba:
  #3 (permalink)  
Antiguo 18/01/2008, 04:17
 
Fecha de Ingreso: enero-2008
Mensajes: 5
Antigüedad: 16 años, 4 meses
Puntos: 0
Re: problema leyendo un archivo con estructuras

muchas gracias _Lucifer_, tienes razón. He corregido un poco el código, pero me sigue pasando lo mismo...el archivo gastos.dts contiene los siguientes registros en binario:

Código:
 |   0 |   Nombre 1 |                     Concepto 1 | 29/ 3/1989 |     0 |
 |   1 |   Nombre 2 |                     Concepto 2 | 30/ 3/1989 |     0 |
 |   2 |   Nombre 3 |                     Concepto 3 | 31/ 3/1989 |     0 |
 |   3 |   Nombre 4 |                     Concepto 4 |  1/ 4/1989 |     0 |
Con esto, la funcion abrir (en rojo las modificaciones) debería mostrarme siempre los 4 resgistros que hay en gastos.dts:

Código:
void abrir() {

    FILE * fp;
    fp = fopen("gastos.dts", "rb");
    _gasto *aux, *auxSIG;
    aux = (_gasto *) malloc(sizeof(_gasto));
    auxSIG = (_gasto *) malloc(sizeof(_gasto));
    aux->sigGasto = auxSIG;

    while (!feof(fp)) {
        fread(aux, sizeof(_gasto), 1, fp);
        printf(" | %3d | %10s | %30s | %2i/%2i/%4i | %5i |\n",
                aux->i, aux->nombre, aux->concepto, aux->fecha.dia,
                aux->fecha.mes, aux->fecha.ano, aux->importe);
        aux = aux->sigGasto;
    }

    fclose(fp);

    free(aux);
    free(auxSIG);
}
Y sin embargo, hay veces que si que me muestra los 4 resgistros, y otras que solo me muestra el primero, de una forma totalmente aleatoria. He probado ya todo lo que se me ocurre, y no consigo arreglarlo :(

La otra duda que me queda es que cuando declaro los punteros a la lista, me toca declarar un puntero para cada elemento que vaya a introducir, y reservarle memoria (en rojo lo que he añadido):

Código:
int main(char argv[], int argc) {

    DB = (_gasto *) malloc(sizeof(_gasto));
    DB->sigGasto = (_gasto *) malloc(sizeof(_gasto));
    DB->sigGasto->sigGasto = (_gasto *) malloc(sizeof(_gasto));
    DB->sigGasto->sigGasto->sigGasto = (_gasto *) malloc(sizeof(_gasto));

    abrir();

    free(DB);
    free(DB->sigGasto);
    free(DB->sigGasto->sigGasto);
    free(DB->sigGasto->sigGasto->sigGasto);
    return 0;
}
Y estoy seguro que se tiene que poder hacer de otra forma, sin tener que declarar 4 punteros y reservarle memoria a los 4...

Mi idea, por si no se entiende bien lo que estoy intentando hacer, es abrir un archivo con un número de registros que yo desconozco, e ir metiendolos en una lista enlazada. Y una vez esten en la lista enlazada, mostrarlos. No los muestro directamente porque después me tocará operar con ellos, por eso los necesito guardar de alguna forma en la memoria, y en la universidad me han dicho que debo hacerlo con listas enlazadas.

Aquí dejo otra vez el código entero con las modificaciones (en rojo) que le he hecho:
Código:
#include <stdio.h>
#define MAX_N 10 /* MAX_NOMBRE */
#define MAX_C 30 /* MAX_CONCEPTO */

typedef struct {
    int dia;
    int mes;
    int ano;
} _fecha;


struct gasto{
    int i;
    char nombre[MAX_N];
    char concepto[MAX_C];
    _fecha fecha;
    float importe;
    struct gasto * sigGasto;
};
typedef struct gasto _gasto;

_gasto *DB;

void abrir();
void guardar(_gasto *);

int main(char argv[], int argc) {

    DB = (_gasto *) malloc(sizeof(_gasto));
    DB->sigGasto = (_gasto *) malloc(sizeof(_gasto));
    DB->sigGasto->sigGasto = (_gasto *) malloc(sizeof(_gasto));
    DB->sigGasto->sigGasto->sigGasto = (_gasto *) malloc(sizeof(_gasto));

    abrir();

    free(DB);
    free(DB->sigGasto);
    free(DB->sigGasto->sigGasto);
    free(DB->sigGasto->sigGasto->sigGasto);
    return 0;
}


// Cada ejecucion da un resultado.
void abrir() {

    FILE * fp;
    fp = fopen("gastos.dts", "rb");
    _gasto *aux, *auxSIG;
    aux = (_gasto *) malloc(sizeof(_gasto));
    auxSIG = (_gasto *) malloc(sizeof(_gasto));
    aux->sigGasto = auxSIG;

    while (!feof(fp)) {
        fread(aux, sizeof(_gasto), 1, fp);
        printf(" | %3d | %10s | %30s | %2i/%2i/%4i | %5i |\n",
                aux->i, aux->nombre, aux->concepto, aux->fecha.dia,
                aux->fecha.mes, aux->fecha.ano, aux->importe);
        aux = aux->sigGasto;
    }

    fclose(fp);

    free(aux);
    free(auxSIG);
}


void guardar(_gasto *guardar) {

    FILE * fp;
    int contador = 0;

    /* Abro el archivo gastos.dts para escritura */
    fp = fopen("gastos.dts", "wb");

    /* Mientras que haya algun registro "gasto" lo escribe en el archivo gastos.dts */
    while (guardar != NULL) {
        fwrite(guardar, sizeof(_gasto), 1, fp);
        guardar = guardar->sigGasto;
        contador++;
    }

    /* Cierra el arhivo */
    fclose(fp);

    /* Libero memoria */
    free(guardar);

    printf("Datos guardados: %i", contador);
}
Muchas gracias,
saludos.
  #4 (permalink)  
Antiguo 18/01/2008, 08:28
Avatar de _Lucifer_  
Fecha de Ingreso: junio-2006
Mensajes: 1.662
Antigüedad: 17 años, 11 meses
Puntos: 28
Re: problema leyendo un archivo con estructuras

Primero pon un poco de orden en todo lo que estas haciendo, tirando código a ver que funciona no vas a llegar muy lejos. Tienes que estructurar bien tu programa y entender el concepto de listas enlazadas y como operar con ellas. Revisa algún manual o tutorial de estructuras de datos en C para que captes mejor los conceptos básicos.

A pesar de las correcciones que hiciste sigues teniendo los mismos problemas, solo pusiste pañitos de agua tibia.

Si buscas en google encontraras mucha info, por ejemplo el primer link que me apareció.

Saludos
__________________
Si crees que no tiene sentido, etonces probablemente lo tenga... :arriba:
  #5 (permalink)  
Antiguo 18/01/2008, 09:32
 
Fecha de Ingreso: enero-2008
Mensajes: 5
Antigüedad: 16 años, 4 meses
Puntos: 0
Re: problema leyendo un archivo con estructuras

De acuerdo, seguiré leyendo...el caso es que lo que me mosquea es que la función abrir de un resultado diferente cada vez que la abro, no sé, no le veo ninguna explicacion.


Un saludo.
  #6 (permalink)  
Antiguo 18/01/2008, 14:17
 
Fecha de Ingreso: enero-2008
Mensajes: 5
Antigüedad: 16 años, 4 meses
Puntos: 0
Re: problema leyendo un archivo con estructuras

nada, no hay manera...

entiendo el funcionamiento de las listas, he creado un programa totalmente distinto, pero sigue dandome problemas en la funcion abrir() (que es la misma pero adaptada). Si alguien pudiera darme algun ejemplo de como leer estructuras de un archivo le estaría eternamente agradecido...solo me vale hacerlo en modo binario, ya que si no, obtengo una única variable con todos los datos de la estructura, y yo necesito ir obteniendo cada estructura que haya en el archivo para insertarla en la lista enlazada.

Aquí os dejo el programa:
Código:
#include <stdio.h>
#define MAX_N 10 /* MAX_NOMBRE */
#define MAX_C 30 /* MAX_CONCEPTO */
#define MAX_R 999 /* MAX_REGISTROS */

typedef struct {
    int dia;
    int mes;
    int anyo;
} _fecha;


struct gasto{
    char i;
    char nombre[MAX_N];
    char concepto[MAX_C];
    _fecha fecha;
    float importe;
    struct gasto * siguiente;
};
typedef struct gasto _gasto;

_gasto *DB;

void menu();
void crear_lista(_gasto **lista);
void mostrar_lista(_gasto *);
void insertar_en_lista(_gasto **lista, _gasto dato);
void borrar_elemento_lista(_gasto **lista, int n_borrar);
void abrir_lista();


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

void crear_lista(_gasto **lista) {
    *lista = NULL;
    abrir_lista();
}

void mostrar_lista(_gasto *lista) {

    if (lista != NULL) {
        printf("\n |   N |     NOMBRE |                       CONCEPTO |      FECHA |  IMPORTE |\n");
        while(lista != NULL) {
            printf(" | %3d | %10s | %30s | %2i/%2i/%4i | %5.2f |\n",
                   lista->i, lista->nombre, lista->concepto, lista->fecha.dia,
                   lista->fecha.mes, lista->fecha.anyo, lista->importe);
            lista = lista->siguiente;
        }
        printf("\n Presione cualquier tecla para volver al menu...\n");
        getch();
    }else{
        printf("\n La lista esta vacia! Presione cualquier tecla para volver al menu...\n");
        getch();
    }
}

void insertar_en_lista(_gasto **lista, _gasto dato) {

    _gasto *p;

    if((*lista) != NULL) {
        insertar_en_lista(&((*lista)->siguiente), dato);
    }else{
        p = (_gasto *) malloc(sizeof(_gasto));
        *p = dato;
        p->siguiente = NULL;
        (*lista) = p;
    }
}

void borrar_elemento_lista(_gasto **lista, int n_borrar) {

    _gasto *borrar, *ant, *sig;

    if ((*lista) == NULL) {
            printf("\n La lista esta vacia! Presiona cualquier tecla para volver al menu...");
            getchar();
            getchar();
            menu();
        }

    if ((*lista)->i != n_borrar) { /* Si no es el registro que quiero borrar... */
        if((*lista)->i == n_borrar-1) { /* ...si es el registro anterior... */
            ant = (_gasto *) malloc(sizeof(_gasto));
            ant = (*lista); /*...lo guardo en ant. */
        }
        borrar_elemento_lista(&((*lista)->siguiente), n_borrar); /* Sigo buscando el registro que quiero... */
    }else{ /* Si si que es el registro que hay que borrar */
        borrar = (_gasto *) malloc(sizeof(_gasto));
        borrar = (*lista); /* lo guardo en borrar */
        sig = (_gasto *) malloc(sizeof(_gasto));
        sig = borrar->siguiente; /* y guardo en sig el registro siguiente al que quiero borrar */
        ant->siguiente = sig; /* y hago que el registro siguiente a ant apunte a sig */
        printf("DONE!");
    }
}

void abrir_lista() {

    FILE * fp;
    fp = fopen("gastos.dts", "rb");

    _gasto *aux;
    aux = (_gasto *) malloc(sizeof(_gasto));
    aux->siguiente = (_gasto *) malloc(sizeof(_gasto));

    while (!feof(fp)) {
        fread(aux, sizeof(_gasto), 1, fp);
        insertar_en_lista(&DB, *aux);
        aux = aux->siguiente;
    }
    aux->siguiente = NULL;

    fclose(fp);

    free(aux);
}

void menu() {

    /* Borrro la pantalla */
    system("CLS");

    /* Declaro las variables encesarias */
    _gasto dato;
    int i = 0, estado = 0, opcion, n_borrar;

    /* Escribo el menu y pido que elija */
    printf("\n MENU\n\n [1] - Introducir elementos en la lista.\n [2] - Mostrar lista\n [3] - Borrar un elemento.\n [0] - Salir.\n\n Elija: ");
    scanf(" %d", &opcion);

    /* Segun lo que elija, hago */
    switch(opcion) {

        /* Salir */
        case 0:
        break;

        /* Insertar elemento en lista */
        case 1:
        do{
            getchar();
            dato.i = i; i++;
            printf("\n Nombre: ");
            gets(dato.nombre);
            printf(" Concepto: ");
            gets(dato.concepto);
            printf(" Fecha (ddmmaaaa): ");
            scanf("%2d%2d%4d", &dato.fecha.dia, &dato.fecha.mes, &dato.fecha.anyo);
            printf(" Importe (€): ");
            scanf("%f", &dato.importe);
            insertar_en_lista(&DB, dato);
            printf("\n Quieres introducir otro gasto? (SI = 1, NO = 0): ");
            scanf("%d", &estado);
        }while (estado);
        puts("\n");
        mostrar_lista(DB);
        menu();
        break;

        /* Mostrar la lista */
        case 2:
        mostrar_lista(DB);
        menu();
        break;

        /* Borrar un registro de la lista */
        case 3:
        printf("\n Introduce el numero del resgistro que quieres borrar: ");
        scanf("%d", &n_borrar);
        borrar_elemento_lista(&DB, n_borrar);
        mostrar_lista(DB);
        menu();
        break;

        /* Si no elige ninguna opcion */
        default:
        menu();
        break;

    }
}
Este codigo me da dos errores. Uno en la funcion borrar_elemento_lista(). El error lo produce la linea marcada en rojo, y no entiendo porque.

El otro, y el que quisiera que alguien me ayudara, es el de la funcion abrir(). No consigo que funcione de ninguna manera, y necesito que lo haga...si alguien pudiera solucionarme este error se lo agradeceria mucho. Yo, después de 2 dias de intentarlo y de acudir a este foro como última solución, ya no se que hacer...

Gracias por adelantado,
saludos.
  #7 (permalink)  
Antiguo 18/01/2008, 16:07
 
Fecha de Ingreso: enero-2008
Mensajes: 5
Antigüedad: 16 años, 4 meses
Puntos: 0
Re: problema leyendo un archivo con estructuras

Bueeeno...vamos progresando. Acabo de conseguir que me funcione la función abrir(), el problema era este:

Código:
void abrir_lista() {

    FILE * fp;
    fp = fopen("gastos.dts", "rb");

    _gasto *aux;
    aux = (_gasto *) malloc(sizeof(_gasto));
    aux->siguiente = (_gasto *) malloc(sizeof(_gasto));

    while (!feof(fp)) {
        fread(aux, sizeof(_gasto), 1, fp);
        insertar_en_lista(&DB, *aux);
        aux = aux->siguiente;
    }
    aux->siguiente = NULL;

    fclose(fp);

    free(aux);
}

y un poco arreglada, se queda así:
Código:
void abrir_lista() {

    FILE * fp;
    fp = fopen("gastos.dts", "rb");

    _gasto *aux;
    aux = (_gasto *) malloc(sizeof(_gasto));

    while (!feof(fp)) {
        fread(aux, sizeof(_gasto), 1, fp);
        insertar_en_lista(&DB, *aux);
    }
    
    borrar_ultimo(&DB); 

    fclose(fp);
    free(aux);
}

void borrar_ultimo(_gasto **lista) {

    _gasto *ant, *aux;
    aux = (*lista);
    ant = NULL;

    while(aux->siguiente != NULL) {
        ant = aux;
        aux = aux->siguiente;
    }
    if (aux->siguiente == NULL) {
        ant->siguiente = NULL;
        free(aux);
    }
}
Como podéis ver, he hecho una nueva función: borrar_ultimo(); que se encarga de borrar el ultimo registro de la lista. Solo funciona si la lista tiene 1 registro o más. Esto lo hago porque cuando leo los registros, me muestra duplicado el ultimo. Supongo que será porque en ningun momento hago que la lista tome el valor NULL, pero no lo se. ¿Alguien me lo puede confirmar?

La función para mostrar la lista es esta:

Código:
void mostrar_lista(_gasto *lista) {

    if (lista != NULL) {
        printf("\n |   N |     NOMBRE |                       CONCEPTO |      FECHA |  IMPORTE |\n");
        while(lista != NULL) {
            printf(" | %3d | %10s | %30s | %2i/%2i/%4i | %5.2f |\n",
                   lista->i, lista->nombre, lista->concepto, lista->fecha.dia,
                   lista->fecha.mes, lista->fecha.anyo, lista->importe);
            lista = lista->siguiente;
        }
        printf("\n Presione cualquier tecla para volver al menu...\n");
        getch();
    }else{
        printf("\n La lista esta vacia! Presione cualquier tecla para volver al menu...\n");
        getch();
    }
}
Supongo que lo que hace que me muestre el ultimo dubplicado es lo que hay en rojo.

Bueno, pues poco a poco voy consiguiendo que vaya funcionando. Ire poniendo aquí mis progresos por si le pudiera servir a alguien en un futuro...

Saludos.
  #8 (permalink)  
Antiguo 21/01/2008, 07:47
Avatar de _Lucifer_  
Fecha de Ingreso: junio-2006
Mensajes: 1.662
Antigüedad: 17 años, 11 meses
Puntos: 28
Re: problema leyendo un archivo con estructuras

¿Ves como comprendiendo bien los conceptos y haciendo todo de forma ordenada salen bien las cosas?

A primera vista no veo nada malo en tu código, tendría que revisarlo con más detalle, sólo dos preguntas:

¿Está leyendo la cantidad de registros correcta?
¿Cuando borras el último registro muestra bien la lista?

Saludos
__________________
Si crees que no tiene sentido, etonces probablemente lo tenga... :arriba:
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 19:46.