0

I have a structure that is like:

typedef struct Student {
    char name[50];
    int roll_no;
    char telephone[50];
    char address[50];
}
Student;

And an array of such structures,

typedef struct SReg {
    Student arr[MAX_STUDENTS];
    int curr_length;    //curr_length stores number of students currently in the student register, MAX is 100
}
SReg;

I have an SReg variable sr. The name field contains names in Surname, Firstname format. For example, Rogers, Steve. I want to be able to sort the structures dumped into a binary file by their firstnames. The contents of sr has been exported to the binary file as follows.

FILE *file = fopen(fname, "wb");
    if (file == NULL) {
        printf("The file cannot be created!");
        return 0;
    }
    if (fwrite(sr, sizeof(Student), sr->curr_length, file) != sr->curr_length) return 0;
    fclose(file);
    return 1;
}

Now I want to implement bubble sort based on the firstnames of the "name" field of the structure. I can get to the first names of students once I can access the name field of the structure using:

char *ptr_i = NULL;
char *ptr_j = NULL;
// To skip surname and get to first name
ptr_i = strchr(sr->arr[i].name, ' ') + 1;
ptr_j = strchr(sr->arr[j].name, ' ') + 1;

// ptr_i and ptr_j contain the two firstnames to be compared, and compare using strcmp
if (strcmp(ptr_i, ptr_j) > 0) {
   Student temp;
   temp = sr->arr[i];
   sr->arr[i] = sr->arr[j];
   sr->arr[j] = temp;
}

But my question is, how do I access the name field of each structure stored in the binary file? I can get the total number of such structures in the file by

fseek(file, 0, SEEK_END);
// get total number of records in file
int total = ftell(file)/sizeof(Student);
rewind(file);

So after that I can use two nested for loops and make them run total number of iterations and get a bubble sort algo. But my question is, how do I actually access the names and compare them? How can I get to the first names? I'm doing this because I need to sort a binary file in place. I've seen that this can probably done using fseek, fwrite and fread, but no clue actually how.

5
  • 1
    Once you have the number of entries create and array to hold them and read then into the array with fread, then sort the array and write it back. With a binary file it's generally better if you write a small header to the file including at least a version number that you'd increment if the struct ever changed and the number of entries. I like to add a 4 byte identifier too so I can tell if the file is indeed the type I expect. Since you don't have that you probably want to be sure the file length % sizeof(Student) is 0. Commented Nov 18, 2022 at 6:19
  • 1
    Your storage design carefully makes it hard to sort the data in the way you want. You should normalize the data (a database design term) and separate the surname and first name into separate fields. That would make sorting much easier, and it doesn't make the presentation any harder — it's easy to arrange to print Surname, Firstname given the two fields. It's hard to print just the surname, or just the first name, if you store them combined. Commented Nov 18, 2022 at 6:25
  • @SupportUkraine Just an assignment I was given, but seems particularly hard in case of binary files. I'm able to do this sort once it has been loaded into the memory, but not keeping them in place. fseek and fread statements are hard to think of, how much to read and what to read Commented Nov 18, 2022 at 6:29
  • Using mmap will make life much easier. Commented Nov 18, 2022 at 6:55
  • 2
    Mmap the file (with read/write permission) and qsort() it like any other in-memory array. Commented Nov 18, 2022 at 7:01

1 Answer 1

2

Think of the file as an byte array, and with the following utitlity functions you're able to read and write from and to the file, from and to specific indices.

e.g.

//reads a student from the specified index
Student* getStudent(FILE *f, long offset, Student *res)
{
    fseek(f, sizeof(Student) * offset, SEEK_SET); //set file position
    fread(res, sizeof(Student), 1, f); //read into res
    return res; //return res
}

//writes the student to the specified index
void setStudent(FILE *f, long offset, Student *student)
{
    fseek(f, sizeof(Student) * offset, SEEK_SET); //set file position
    fwrite(student, sizeof(Student), 1, f); //write student
}

//iterate over all students stored in the file
void foreachStudent(FILE *f, void (*callback)(Student*))
{
    fseek(f, 0, SEEK_END); //set pos to end
    int num = ftell(f) / sizeof(Student); //num students in file
    rewind(f); //set pos to begin
    Student student; //temporary object (buffer)
    for (int i=0; i < num; ++i) { //for num elements
        fread(&student, sizeof(Student), 1, f); //read next element
        callback(&student); //invoke callback
    }
}

Note: the code above assumes that there are no errors.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.