0

I've been having a problem with a C program that works with a binary file, by passing it's contents to a linked list. When the file is empty, I just create the linked list and manipulate it in the program, that mostly works fine (debugging the solutions as we speak). And when I am ready to leave the program, it moves the linked list back into the file with apparently no problems. But when I try to open the program again, it writes what it's on the file plus one more linked list node (if there are 2 registers saved in the binary file, it will create 3 nodes, 2 with the file info and 1 totally empty. Does anyone know why this happens and what I can do to fix it??

Here is the code:

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

typedef struct {
    int dia;
    int mes;
    int anio;
} fecha;

typedef struct {
    int protocolo;
    char paciente [50];
    char medico [50];
    int tieneOS;
    char obraSocial [50];
    int montoOS;
    int montoPaciente;
    fecha fechaAnalisis;
} cobro;

typedef struct Tnodo{
    cobro info;
    struct Tnodo *next;
} nodo;

fecha leerFecha();
void cargarInforme(nodo **c, fecha f);
void modificarInforme(nodo *c);
void eliminarInforme(nodo **c);
void buscarProtocolo(nodo *c);
void listarInformes(nodo *c);
void listarMontosOS(nodo *c);
void myflush ( FILE *in );
void mypause ( void );
int sumaMontoOS(nodo *c, char nOS[20], int mT);

FILE *archivo;

int main () {
    int opcion;
    int size;
    fecha fechaActual;
    fechaActual = leerFecha();
    printf("La fecha ingresada es: %d/%d/%d", fechaActual.dia, fechaActual.mes, fechaActual.anio);
    nodo *head = malloc(sizeof(nodo));
    nodo *cursor = head->next;
    archivo = fopen("cobros.dat", "ab+");

    if (NULL != archivo) {
        fseek (archivo, 0, SEEK_END);
        size = ftell(archivo);
    }
    if (size == 0) {
        printf("El archivo no existe. Creando Lista....\n");
        sleep(3);
        cursor = head;
    } else {
        printf("El archivo existe. Cargando Lista.\n");
        sleep(3);
        rewind (archivo);
        cursor = head;
        while (!feof(archivo)) {
            nodo *nuevo = malloc(sizeof(nodo));
            fread(&nuevo->info, sizeof(cobro), 1, archivo);
            cursor->next = nuevo;
            cursor = cursor->next;
        }
    }
   

    do
    {
        system ("clear");
        printf ("MENU DE OPCIONES:\n\n");
        printf ("1. Cargar Informe\n");
        printf ("2. Modificar Informe\n");
        printf ("3. Eliminar Informe\n");
        printf ("4. Buscar un Protocolo\n");
        printf ("5. Listar Informes\n");
        printf ("6. Listar Montos OS por mes\n");
        printf ("7. Guardar y Salir\n");
        printf ("Elige una opcion: ");
        scanf ("%d", &opcion);

        switch (opcion)
        {
            case 1:
                system ("clear");
                cursor = head;
                cargarInforme(&cursor, fechaActual);
                break;
            case 2:
                system ("clear");
                cursor = head;
                modificarInforme(cursor);
                break;
            case 3:
                system ("clear");
                cursor = head->next;
                eliminarInforme(&cursor);
                break;
            case 4:
                system ("clear");
                break;
            case 5:
                system ("clear");
                cursor = head->next;
                listarInformes(cursor);
                break;
            case 6:
                system ("clear");
                cursor = head->next;
                listarMontosOS(cursor);
                break;
            case 7:
                system ("clear");
                cursor = head->next;
                rewind (archivo);
                while (cursor != NULL) {
                    fwrite(&cursor->info, sizeof(cobro), 1, archivo);
                    cursor = cursor->next;
                }
                fclose(archivo);
                break;
            default:
                system ("clear");
                printf ("La opcion ingresada no es correcta. Por favor intente nuevamente.");
                break;
        };
    }while (opcion != 7);

}
    
    

fecha leerFecha() {
    fecha f;
    printf("Ingrese la fecha actual (dd/mm/aaaa): ");
    scanf("%d/%d/%d", &f.dia, &f.mes, &f.anio);
    return f;
}

void cargarInforme(nodo **c, fecha f) {
    nodo *aux = malloc(sizeof(nodo));
    while ((*c)->next != NULL) {
        (*c) = (*c)->next;
    }
    aux->info.protocolo = (*c)->info.protocolo + 1;
    printf("Ingrese el nombre del paciente: ");
    scanf("%s", aux->info.paciente);
    printf("Ingrese el nombre del medico: ");
    scanf("%s", aux->info.medico);
    printf("Tiene obra social? (1 = si, 0 = no): ");
    scanf("%d", &aux->info.tieneOS);
    if (aux->info.tieneOS == 1) {
        printf("Ingrese el nombre de la obra social: ");
        scanf("%s", aux->info.obraSocial);
        printf("Ingrese el monto de la obra social: ");
        scanf("%d", &aux->info.montoOS);
    } else {
        strcpy (aux->info.obraSocial, "No tiene");
        aux->info.montoOS = 0;
    }
    printf("Ingrese el monto a pagar por el paciente: ");
    scanf("%d", &aux->info.montoPaciente);
    aux->info.fechaAnalisis = f;
    (*c)->next = aux;
}

void modificarInforme(nodo *c) {
    int protocolo;
    int o;
    printf("Ingrese el protocolo del informe a modificar: ");
    scanf("%d", &protocolo);
    while (c != NULL) {
        if (c->info.protocolo == protocolo) {
            do {
                system ("clear");
                printf ("QUE CAMPO DESEA MODIFICAR? (NO PUEDE MODIFICAR FECHA NI NUMERO DE PROTOCOLO):\n\n");
                printf ("1. Nombre del Paciente\n");
                printf ("2. Nombre del Medico\n");
                printf ("3. Estado de Obra Social (Tiene OS o no, Nombre y Monto a pagar)\n");
                printf ("4. Monto a Pagar del Paciente\n");
                printf ("5. No deseo modificar mas, Guardar y Salir\n");
                printf ("Elige una opcion: ");
                scanf ("%d", &o);

                switch (o) {
                    case 1:
                        system ("clear");
                        printf("Ingrese el nuevo nombre del paciente: ");
                        scanf("%s", c->info.paciente);
                        break;
                    case 2:
                        system ("clear");
                        printf("Ingrese el nuevo nombre del medico: ");
                        scanf("%s", c->info.medico);
                        break;
                    case 3:
                        system ("clear");
                        printf("Quiere cambiar status de obra social? (1 = si, 0 = no): ");
                        scanf("%d", &c->info.tieneOS);
                        if (c->info.tieneOS == 1) {
                            printf("Ingrese el nuevo nombre de la obra social: ");
                            scanf("%s", c->info.obraSocial);
                            printf("Ingrese el nuevo monto de la obra social: ");
                            scanf("%d", &c->info.montoOS);
                        } else {
                            strcpy (c->info.obraSocial, "No tiene");
                            c->info.montoOS = 0;
                        }
                        break;
                    case 4:
                        system ("clear");
                        printf("Ingrese el nuevo monto a pagar por el paciente: ");
                        scanf("%d", &c->info.montoPaciente);
                        break;
                    case 5:
                        system ("clear");
                        break;
                    default:
                        system ("clear");
                        printf ("La opcion ingresada no es correcta. Por favor intente nuevamente.");
                        break;
                }
            }while (o != 5);
            break;
        }
        c = c->next;
    }
}

void eliminarInforme(nodo **c) {
    int protocolo;
    nodo *aux;
    printf("Ingrese el protocolo del informe a eliminar: ");
    scanf("%d", &protocolo);
    while ((*c) != NULL) {
        if ((*c)->info.protocolo == protocolo) {
            aux = (*c);
            (*c) = (*c)->next;
            free(aux);
            break;
        }
        (*c) = (*c)->next;
    }
}

void listarInformes(nodo *c) {
    while (c != NULL) {
        printf("Protocolo: %d\n", c->info.protocolo);
        printf("Paciente: %s\n", c->info.paciente);
        printf("Medico: %s\n", c->info.medico);
        printf("Tiene OS: %d\n", c->info.tieneOS);
        printf("OS: %s\n", c->info.obraSocial);
        printf("Monto OS: %d\n", c->info.montoOS);
        printf("Monto Paciente: %d\n", c->info.montoPaciente);
        printf("Fecha Analisis: %d/%d/%d\n\n", c->info.fechaAnalisis.dia, c->info.fechaAnalisis.mes, c->info.fechaAnalisis.anio);
        c = c->next;
    }
    myflush(stdin);
    mypause();
}

void listarMontosOS(nodo *c) {
    int mes;
    int cantOS = 0;
    typedef struct {
        char nombreOS[20];
        int montoTotal;
    } os;
    typedef struct {
        os o [100];
        int cantOS;
    } reg;
    reg r;
    r.cantOS = 0;
    int i = 0;
    printf("Ingrese el mes a consultar (1-12): ");
    scanf("%d", &mes);
    while (c->next != NULL) {
        if (c->info.tieneOS == 1 && c->info.fechaAnalisis.mes == mes) {
            for (i = 0; i < r.cantOS; i++)
            {
                if (strcmp(r.o[i].nombreOS, c->info.obraSocial) == 0) {
                    r.o[i].montoTotal = sumaMontoOS(c, r.o[i].nombreOS, r.o[i].montoTotal);
                    break;
                }
            }
            if (r.cantOS > 0)
            {
                cantOS = r.cantOS - 1;
            }
            if (i == cantOS)
            {
                strcpy(r.o[i].nombreOS, c->info.obraSocial);
                r.o[i].montoTotal = sumaMontoOS(c, r.o[i].nombreOS, r.o[i].montoTotal);
                r.cantOS++;
            }
        }
        c = c->next;
    }
    for (int j = 0; j < r.cantOS; j++)
    {
        printf("El monto total a pagar por la obra social %s es de: %d\n", r.o[i].nombreOS, r.o[i].montoTotal);
        myflush(stdin);
        mypause();
    }
    
   
}


int sumaMontoOS(nodo *c, char nOS[20], int mT) {
    if (c == NULL){
        return mT;
    }
    else if ((c->info.tieneOS == 1) && (strcmp(c->info.obraSocial, nOS) == 0)) {
        return sumaMontoOS(c->next, nOS, mT + c->info.montoOS);
    }
}

void myflush ( FILE *in )
{
  int ch;

  do
    ch = fgetc ( in ); 
  while ( ch != EOF && ch != '\n' ); 

  clearerr ( in );
}

void mypause ( void ) 
{ 
  printf ( "Apriete Enter para continuar . . ." );
  fflush ( stdout );
  getchar();
} 

  • `while (!feof(archivo))` does not do what you imagine, and is a frequent beginner's mistake. It causes the last item to be entered twice. If you had *checked* the return value from `fread` you would have discovered that on the last iteration, it did not read anything. *Always* check that input functions do their job. – Weather Vane Mar 03 '23 at 23:35
  • 1
    The way this should be done is to drive the loop with `fread`, such as `while(fread(&nuevo->info, sizeof(cobro), 1, archivo) == 1) { /* loop body */ }` – Weather Vane Mar 03 '23 at 23:37
  • Thank you very much for your feedback. After some digging I realized what the problem was, and after trying by myself I ended up solving it by adding "if (feof(archivo) then break" after the fread inside the cycle, thus ending the loop when fread gave value 0 and triggering feof, but your implementation is way cleaner. I'll change it to that probably. Thanks again :D – Gaspar Bosch Mar 04 '23 at 22:59
  • Yes, the `feof` can be used *after* the `fread` when it returns 0, to distinguish between EOF and an error. – Weather Vane Mar 04 '23 at 23:02

0 Answers0