2

I'm building a database tool and all I want to do is write a structure to a file in binary and then read it in again. The following is the closest I could find on the web for doing that, but it has major problems:

#include <fstream>
#include <iostream>
#include <vector>
#include <string.h>

using namespace std;


typedef struct student
{
    char name[10];
    int age;
    vector<int> grades;
}student_t;

int main()
{
    student_t apprentice[3];  
    strcpy(apprentice[0].name, "john");
    apprentice[0].age = 21;
    apprentice[0].grades.push_back(1);
    apprentice[0].grades.push_back(3);
    apprentice[0].grades.push_back(5);    

    strcpy(apprentice[1].name, "jerry");
    apprentice[1].age = 22;
    apprentice[1].grades.push_back(2);
    apprentice[1].grades.push_back(4);
    apprentice[1].grades.push_back(6);

    strcpy(apprentice[2].name, "jimmy");
    apprentice[2].age = 23;
    apprentice[2].grades.push_back(8);
    apprentice[2].grades.push_back(9);
    apprentice[2].grades.push_back(10);

    // Serializing struct to student.data
    ofstream output_file("students.data", ios::binary);
    output_file.write((char*)&apprentice, sizeof(apprentice));
    output_file.close();

    // Reading from it
    ifstream input_file("students.data", ios::binary);
    student_t master[3];
    input_file.read((char*)&master, sizeof(master));   

    apprentice[0].grades[0]=100; // ALTERING THE INPUT STRUCTURE AFTER WRITE

    for (size_t idx = 0; idx < 3; idx++)
    {
        // If you wanted to search for specific records, 
        // you should do it here! if (idx == 2) ...

        cout << "Record #" << idx << endl;
        cout << "Name: " << master[idx].name << endl;
        cout << "Age: " << master[idx].age << endl;
        cout << "Grades: " << endl;
        for (size_t i = 0; i < master[idx].grades.size(); i++)
           cout << master[idx].grades[i] << " ";
        cout << endl << endl;
    }

    return 0;
}

This seems to write the file, read it back and then print to screen but unfortunately: Firstly the program crashes with a debug assertion fail (dbgdel.cpp line 52) when it tries to close, and secondly, altering the input structure after writing (as I have in the example) alters the supposedly read structure. I guess what is happening is that somehow "Data" and "inData" are the same thing (which would explain the crash as it would try to delete the same thing from memory twice). Can anyone get this working? I've tried everything I can think of.

moooeeeep
  • 31,622
  • 22
  • 98
  • 187
john boyd
  • 21
  • 1
  • 2

3 Answers3

0

The problem is that your structure is dynamic (due to the vector). This is always going to complicate things, because you are actually storing a char *. The vector is a complex data structure. You can't just mask it as a char * and expect it to represent the elements. So you aren't even storing what you need. I would suggest you change the vector to something like int grades[NO_OF_SUBJECTS]. That should work fine.

Jayanth Koushik
  • 9,476
  • 1
  • 44
  • 52
0

Things you need to know:

The push_backs you do to vector will not increase the sizeof(apprentice). Vector is not nested linearly inside the struct, vector internally allocates memory. You can consider vector object (and many other STL container objects) as something like a pointer.

You need to use a constant sized array instead of vector.

Alternatively, you could dump each vector component to the file, and parse them with push_backs while you are reading them back.

Etherealone
  • 3,488
  • 2
  • 37
  • 56
0

As others have pointed out, vector is more like a pointer so sizeof won't work. You'll either have to use a static C array with a fixed number of grades (sizeof will work there) or you'll have to serialize the number of grades by writing `grades.size() to file for each student and then write each grade.

Then when you read back you'll:

  1. read the name
  2. read the age
  3. read the number of grades
  4. knowing how many grades to read, read each grade and push_back into your student

You could also allow for variable sized names either by null terminating and inching along reading chars until you hit '\0' or you could serialize the name length and read it as above.

This is more tedious than what you have above. But you trade flexibility for complexity.

emsr
  • 15,539
  • 6
  • 49
  • 62