Search code examples
cstructfwritefread

Reading a binary file and show the entries


I may need your help to solve this issue... I'm learning about C and find a problem, how to properly read a binary file.

I have a struct array filled, then wrote on binary file and try to read it, but this isn't show nothing.

The code is in pastebin

My code

#include <stdio.h>
#include <ctype.h>
#include <math.h>

typedef struct {
    int id;
    char name[100];
    char document[14];
    float testGrade;
} Candidate;

typedef struct {
    int id;
    float testGrade;
} Grade;

FILE * openFile(char filename[100], char filemode[3]) {
    FILE *p = fopen(filename, filemode);

    if (!p) {
        printf("Error to open %s file. \nThe program will be closed.",filename);
        exit(1);
    }
}


void addCandidate(int newId, int numbersOfNewCandidate, char candidateFilename[100], char gradeFilename[100]) {
    int counter = 0;
    float testGrade = 0;

    Candidate*candidate;
    candidate= malloc(sizeof(Candidate) * numbersOfNewCandidate);

    Grade *grade;
    grade= malloc(sizeof(Grade) * numbersOfNewCandidate);

    for(;counter< numbersOfNewCandidate; counter++) {
        system("@cls||clear");
        printf("Adding  #%d:",newId);
        printf("\n---------------\n");

        printf("\nName of candidate: ");
        gets(&candidate[counter].name);

        printf("\nDocument: ");
        gets(&candidate[counter].document);

        do {
            printf("\nTest grade (between 0 and 10): ");
            scanf("%f",&testGrade);
            printf("\n---------------\n");

            if (testGrade < 0 || testGrade > 10) {
                printf("\nERROR!\nTest grade %.2f invalid, please try again with a grade between 0 and 10.\n",testGrade);
            }

        } while(testGrade < 0 || testGrade > 10);

        candidate[counter].id = newId;
        grade[counter].id = newId;
        candidate[counter].testGrade = testGrade;
        grade[counter].testGrate = testGrade;
        newId++;
        fflush(stdin);
    }

    FILE *candidateFile = openFile(candidateFilename, "a+b");
    fwrite(candidate, sizeof(candidate),numbersOfNewCandidate, candidateFile );

    FILE *gradeFile = openFile(gradeFilename, "a+b");
    fwrite(grade, sizeof(grade),numbersOfNewCandidate, gradeFile );

    fclose(candidateFile);
    fclose(gradeFile);

    free(candidate);
    free(grade);
}

void showCandidate(int typeOfSearch, char valueToSearch[100]) {}

void listAllCandidates(char candid[100]) {
    FILE *fp = openFile(candidateFilename, "rb");

    //fseek(fp,0,SEEK_SET);

    Candidate *candidate = NULL;

    candidate = malloc(sizeof(Candidate) + 1);

    while(fread(&candidate,sizeof(Candidate),1,fp) == 1) {
        printf("\n\nId: %d \nName: %s \nDocument: %s \nGrade: %.2f",candidate->id,candidate->name,candidate->document, candidate->testGrade);
    }
    getche();
    free(candidate);
}

void main(){
    int lastId = 0;
    char candidateFilename[100] = "candidates.bin";
    char gradeFilename[100] = "classificationList.bin";
    char option;

    do {
        system("@cls||clear");
        printf("Menu: \n");
        printf("1 - Add candidates \n");
        // printf("2 - Search by name \n");
        // printf("3 - Search by document  \n");
        // printf("---------------------------\n");
        // printf("4 - Show Max Grade, Minimum, Avg \n");
        printf("5 - List candidates \n");
        printf("6 - Erase files \n");
        printf("---------------------------\n");
        printf("S - Exit \n");
        printf("\n\n");

        option = toupper(getche());

        switch(option) {
            case '1':
                system("@cls||clear");

                int numbersOfNewCandidate = 0;
                int newId = 0;

                printf("Home > Add candidates\n\n");
                printf("Please give the number of new candidates: ");
                scanf("%d",&numbersOfNewCandidate);
                newId = lastId;
                lastId += numbersOfNewCandidate;

                fflush(stdin);
                addCandidate(newId + 1, numbersOfNewCandidate, candidateFilename, gradeFilename);

                printf("\n\nAdding new candidates: Finished \n");
            break;
            // case '2':
            //     printf("\noption %c@\n",option);
            // break;
            // case '3':
            //     printf("\noption %c#\n",option);
            // break;
            // case '4':
            //     printf("\noption %c?\n",option);
            // break;
            case '5':
                listAllCandidates(candidateFilename);
            break;
            case '6':
                remove(candidateFilename);
                remove(gradeFilename);
                printf("\nRemoved!!\n");
            break;
            case 'S':
                printf("\noption %c, the program will be ended...\n",option);
            break;
            default:
                printf("\nWrong option!!\n");
            break;
        }
    } while (option != 'S');
}

Please, if you find other issues about my code, try to elucidate me about it.. I already tryed it too, but nothing yet.

while(!feof(fp)) {
    fread(&candidate,sizeof(Candidate),1,fp);
    printf("\n\nId: %d \nName: %s \nDocument: %s \nGrade: %.2f",candidate->id,candidate->name,candidate->document, candidate->testGrade);
} 

Solution

  • Candidate *candidate = NULL;
    candidate = malloc(sizeof(Candidate) + 1);
    while(fread(&candidate,sizeof(Candidate),1,fp) == 1) {...}
    free(candidate);
    

    The first parameter in fread should be a pointer fread(void*,...), and candidate is already declared as a pointer, it should not have a reference operator. The correct usage is:

    while(fread(candidate,sizeof(Candidate),1,fp) == 1) {...}
    

    Note that there is no reference & operator in front of candidate


    Sometime you see a reference operator, but that's a different case like this:

    Candidate cand;
    while(fread(&cand,sizeof(cand),1,fp) == 1) {...}
    

    This is an easier method because cand does not need to be allocated with malloc and it does not need free


    Unnecessary functions introduce more errors:

    FILE * openFile(char filename[100], char filemode[3]) {
        FILE *p = fopen(filename, filemode);
        if (!p) {
            printf("Error to open %s file. \nThe program will be closed.",filename);
            exit(1);
        }
    }
    

    This function is supposed to return a file pointer. Also function parameter can be simply written as const char* filename and const char *filemode. Example:

    FILE * openFile(const char* filename, const char* filemode) {
        FILE *p = fopen(filename, filemode);
        if (!p) printf("Error to open %s file. \nThe program will be closed.",filename);
        return p;
    }
    

    I would get rid of this function altogether because it's basically useless. Just use fopen. Make sure to close the handle with fclose when you are done.


    grade[counter].testGrate = testGrade;
                          ^
    

    This is a typo, it should be grade[counter].testGrade. It is recommended to compile the program with warning level set to maximum, or at least set to level 4. The compiler will tell about about the typos, errors and warnings. You have to be able to compile the program with zero errors and zero warnings. Here is a simple version:

    void listAllCandidates(char* candidateFilename)
    {
        FILE *fin = fopen(candidateFilename, "rb");
        Candidate cand;
        while(fread(&cand, sizeof(cand), 1, fin))
            printf("%s, %s, %d, %f\n", cand.name, cand.document, cand.id, cand.testGrade);
        fclose(fin);
    }
    
    int main() 
    {
        char candidateFilename[] = "file.bin";
    
        FILE *fout = fopen(candidateFilename, "ab");
        Candidate cand;
    
        strcpy(cand.name, "name1");
        strcpy(cand.document, "document1");
        cand.id = 1;
        cand.testGrade = 1.1f;
        fwrite(&cand, sizeof(cand), 1, fout);
    
        strcpy(cand.name, "name2");
        strcpy(cand.document, "document2");
        cand.id = 2;
        cand.testGrade = 2.2f;
        fwrite(&cand, sizeof(cand), 1, fout);
    
        fclose(fout);
    
        listAllCandidates(candidateFilename);
    
        printf("\n");
        return 0;
    }