1

The program only works for the first time. What was supposed to happen the second time was to add the same data to the binary file but that doesn't happen.

First run: It runs normal and it shows that it writed to the file.

Secound run: It writes to the file but doesnt read.

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

typedef struct {
    char *name, *role, *course;
    int year, id;
} StudentFile;

void saveBin(StudentFile *studentsFile, int lines){
    FILE *file = fopen("studentsx.bin","ab");
    if (!file) {
        printf("\n\n\tImposible to open file. \n\n");
        exit(1);
    }

    for (int i = 0; i < lines; i++){
        fwrite(&studentsFile[i], sizeof(StudentFile), 1, file);
    }
    fclose(file);
}

void readBin(){
    StudentFile *studentsFile = malloc(sizeof(StudentFile)*5000);
    FILE *file = fopen("studentsx.bin","rb");
    if (!file) {
        printf("\n\n\tImposible to open file. \n\n");
        exit(1);
    }

    int j = 0;

    while (fread(&studentsFile[j], sizeof(StudentFile), 1, file)){
        printf("\nLine read %d: %s\t%s\t%d\t%d\t%s", j+1, studentsFile[j].name, studentsFile[j].role, studentsFile[j].year, studentsFile[j].id, studentsFile[j].course);
        j++;
    }

    fclose(file);
}

void main(){
    StudentFile *studentsFile = malloc(sizeof(StudentFile)*2);
    int lines = 0;

    studentsFile[0].name = "John";
    studentsFile[0].role = "Gamer";
    studentsFile[0].year = 1999;
    studentsFile[0].id = 1;
    studentsFile[0].course = "IOT";
    studentsFile[1].name = "Piter";
    studentsFile[1].role = "GamerXL";
    studentsFile[1].year = 1991;
    studentsFile[1].id = 2;
    studentsFile[1].course = "IOTXL";
    lines = 2;

    saveBin(studentsFile, lines);
    readBin();
}
  • The construction of your loop is very odd. It seems you are aware that the read loop is entered 3 times and that `j == 3` after the loop. Rather than doing `for( i = 0; i < j-1 ...`, it would be better to *fix the loop* and write `for( i = 0; i < j ...`. IOW, you are using `feof` incorrectly: https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong – William Pursell Dec 05 '22 at 20:35
  • you are opening the file for appending, so anything new is written to the end of the file. Try doing a proper read loop and reading all the data to see what's there. – William Pursell Dec 05 '22 at 20:37
  • @WilliamPursell I just updated the code. – Pedro Tavares Dec 05 '22 at 20:43
  • You also seem to be writing pointers to the file. Pointers contain addresses, which are specific to the instance of the application being executed. Reading that file from a different process, even if it's the same program, isn't going to work reliably. – Ulrich Eckhardt Dec 05 '22 at 21:10
  • So should I use arrays instead of pointers or exists a way to do with pointers. – Pedro Tavares Dec 05 '22 at 21:18

2 Answers2

2

You are writing pointers, not strings. fwrite writes single contiguous array of memory. In your case the StudentFiles and actual strings are scattered all over the static memory and heap memory.

Consider your struct:

typedef struct {
  char *name, *role, *course;
  int year, id;
} StudentFile;

it looks something like this in memory:

[<pointer to name><pointer to role><pointer to course><year><id>]

somewhere else in a different block of memory:

[John\0Gamer\0\OIT\o.......]

You wrote the first block above and left out the second one.

There are multiple approaches to this problem and we usually name them "serialization" - take your complex data structure and serialize it into a linear file.

One of the approaches is to allocate fixed size block within your structure StudentFile:

#define MAX_NAME 100
#define MAX_ROLE 100
#define MAX_COURSE 100
typedef struct {
  char name[MAX_NAME];
  char role[MAX_ROLE];
  char course[MAX_COURSE];
  int year, id;
} StudentFile;

then strings name, role and course will be inside of StudentFile:

[<100 bytes for name><100 bytes for role><100 bytes for course><year><id>]

this is contiguous block of memory and if can be written using single call to fwrite like you did.

But you won't be able to assign strings like you did with

studentsFile[i].name = "John";

C has strncpy for that:

strcpy(studentsFile[0].name, "John", MAX_NAME);

Another approach is to have several calls to fwrite. For every string, you write length first, then the string itself. For primitive types like int you just write that int.

First you gather strings from different locations pointed by the pointers:

size_t nameLen = strlen(studentsFile[i].name) + 1;/* +1 for the final zero*/
fwrite(&nameLen, sizeof(size_t), 1, file);
fwrite(studentsFile[i].name, nameLen, 1, file);
size_t roleLen = strlen(studentsFile[i].role) + 1;
fwrite(&roleLen, sizeof(size_t), 1, file);
fwrite(studentsFile[i].role, roleLen, 1, file);
size_t courseLen = strlen(studentsFile[i].course) + 1;
fwrite(&courseLen, sizeof(size_t), 1, file);
fwrite(studentsFile[i].course, courseLen, 1, file);

Then you write primitive types:

fwrite(&studentsFile[i].year, sizeof(int), 1, file);
fwrite(&studentsFile[i].id, sizeof(int), 1, file);

Next time when you read the file, you rely on the order of writes and read the fields back in the same order:

size_t nameLen;
fread(&nameLen, sizeof(size_t), 1, file);
char *name = malloc(nameLen);
fread(name, nameLen, 1, file);
size_t roleLen;
fread(&roleLen, sizeof(size_t), 1, file);
char *role = malloc(roleLen);
fread(role, roleLen, 1, file);
size_t courseLen;
fread(&courseLen, sizeof(size_t), 1, file);
char *course = malloc(courseLen);
fread(course, courseLen, 1, file);
int year;
fread(&year, sizeof(int), 1, file);
int id;
fread(&id, sizeof(int), 1, file);

printf("\nLine read %d: %s\t%s\t%d\t%d\t%s", j+1, name, role, year, id, course);
fukanchik
  • 2,811
  • 24
  • 29
1

The problem lies somewhere else: Think carefully, what is your code doing with fwrite() here?

typedef struct {
    char *name, *role, *course;
    int year, id;
} StudentFile;

fwrite(&studentsFile[i], sizeof(StudentFile), 1, file);

What does the file content look like after writing a single element from studentFile?

  1. Three strings and two integers (in their binary form)
  2. Three pointers to somewhere and two integers (all in their binary forms)
iBug
  • 35,554
  • 7
  • 89
  • 134
  • I really don't understand. Can you try to explain the problem better? @iBug – Pedro Tavares Dec 05 '22 at 21:08
  • @PedroTavares Maybe you can have a better understanding if you inspect the file content after the first run. Use a hexadecimal viewer (WinHex or Unix command `hexdump -Cv`). – iBug Dec 05 '22 at 21:10
  • I just tested but I never used hexadecimal viewer but it shows me on the right a bunch of random characters – Pedro Tavares Dec 05 '22 at 21:16