Home > Software design >  C file Fwrite and fread just work for the first time run
C file Fwrite and fread just work for the first time run

Time:12-06

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();
}

CodePudding user response:

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);

CodePudding user response:

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)
  • Related