How to write objects to file...

Please, help me. I'm in serious trouble.

I have the following class-
1
2
3
4
5
6
7
8
9
10
class student {
    string name;
    int roll;
    double average;
    double grade;
    vector<double> scores;
    vector<string> assignments;
/////////////////////////////////
// functions here
};


I have a globally declared vector like this-
vector<student> grade_book; // global
and a variable to store the size of the vector-
int top; // global Now, every time I open the program, I want to load from a file to the vector. And I want to save the vector every time I exit the program.
I have this function for load-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void load_book()
{
    FILE* buf;

    if( ( buf = fopen( "D:\\Data.dat", "rb" ) ) == NULL ) 
    {
        return; 
    } // file does not exist for the first time, so function returns 

    student temp;
    fread(&top, sizeof(top), 1, buf);
    
    for(int i = 0 ; i < top; i++)
    {
        fread(&temp, sizeof(student), 1, buf);
        grade_book.push_back(temp);
    }

    fclose(buf);
}

and this one for save-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void save_book()
{
    FILE* buf;

    if( ( buf = fopen( "D:\\Data.dat", "wb" ) ) == NULL )
    {
        cout << "Cannot open file.\n";
        exit(1);
    }

    student temp;
    top = grade_book.size();
    fwrite(&top, sizeof(top), 1, buf);
    for(int i = 0; i < grade_book.size(); i++)
    {
        fwrite(&grade_book[i], sizeof(student), 1, buf); // write the file
    }
    fclose(buf);
}


But, my program is not working at all and crashing repeatedly when run.
Please please please, give me a solution...
Last edited on
First why are you using C file input and output operations? You really should be using C++ streams instead.

Second trying to use read() or write() with std::string and std::vector will not produce the desired action. You'll need to serialize these non-POD classes.

And please use code tags when posting code.
Last edited on
Please let me know in detail...
Last edited on
Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <iostream>
#include <string>
#include <vector>
#include <sstream>

struct student
{
    std::string name;
    int roll;
    double average;
    double grade;
    std::vector<double> scores;
    std::vector<std::string> assignments;
};

// write student object into a stream
std::ostream& operator<< ( std::ostream& stm, const student& stu )
{
    stm << stu.name << '\n' // name on line 1
        << stu.roll << ' ' << stu.average << ' ' << stu.grade << '\n' ; // line 2

    stm << stu.scores.size() << '\n' ; // #scores on line 3
    for( double s : stu.scores ) stm << s << ' ' ; // scores on line 4
    stm << '\n' ;

    stm << stu.assignments.size() << '\n' ; // #assignments on the next lime
    for( const std::string& s : stu.assignments ) stm << s << '\n' ; // one assignment per line

    return stm ;
}

// read student object from a stream
std::istream& operator>> ( std::istream& stm, student& stu )
{
    // skip leading empty lines, read name from non-empty line 1
    while( std::getline( stm, stu.name ) && stu.name.empty() ) ;
    stm >> stu.roll >> stu.average >> stu.grade ; // line 2

    std::size_t score_sz ;
    if( stm >> score_sz ) // #scores from line 3
    {
        stu.scores.resize(score_sz) ;
        for( double& s : stu.scores ) stm >> s ; // read score_sz scores

        std::size_t assignment_sz ;
        if( stm >> assignment_sz >> std::ws ) // read #grades, throw away the new line
        {
            stu.assignments.resize(assignment_sz) ;
            for( std::string& s : stu.assignments ) std::getline( stm, s ) ; // assignment_sz assignments
        }
    }

    if( !stm ) stu = {} ; // input failed; clear everything

    return stm ;
}

int main()
{
    student s { "psychameron ", 1234, 72.34, 81.43, { 1.2, 3.4, 5.6 }, { "ab", "cd ef", "ghijk" } } ;
    std::cout << s << '\n' ;

    std::istringstream stm( "some name\n"
                            "555 48.64 93.45 \n"
                            " 5 \n"
                            "1.0  2.3 4.5 6.7 8.9\n"
                            "3 \n"
                            "hello there\n"
                            "hello again\n"
                            "bye\n" ) ;
    student s2 ;
    if( stm >> s2 ) std::cout << "--------\n" << s2 << '\n' ;

    std::stringstream test_stm ;

    // write vector of students to a stream
    std::vector<student> seq { s, s2 } ;
    for( const student& s : seq ) test_stm << s << '\n' ;

    // read them back
    seq.clear() ;
    student temp ;
    while( test_stm >> temp ) seq.push_back(temp) ;

    // veryfy that they have been read back
    std::cout << "\n----------------------\n" ;
    for( const student& s : seq ) std::cout << s << "\n-----------\n" ;
}

http://coliru.stacked-crooked.com/a/f6242419ef7f8a6e
Thanks, this is too much for me to take in...
Can you please explain why this doesn't work-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void load_book()
{
    ifstream in("D:\\data.dat", ios::in | ios::binary);

    if(!in)
    {
        cout << "returning\n";
        return ;
    }

    ifstream inTop("D:\\top.dat", ios::in | ios::binary);
    int top;

    inTop.read((char*)&top, sizeof(top)); // read the top
    inTop.close();

    student temp;
    for(int i = 0; i < top; i++)
    {
        in.read((char*)&temp, sizeof(temp)); // read the objects
        grade_book.push_back(temp);
    }
    in.close();
}


And this-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void save_book()
{
    ofstream out("D:\\data.dat", ios::out | ios::binary); // output stream

    ofstream outTop("D:\\top.dat", ios::out | ios::binary); // output stream

    int top = grade_book.size();
    outTop.write((char*)&top, sizeof(top));
    outTop.close();

    for(int i = 0; i < top; i++) out.write((char*)&grade_book[i], sizeof(grade_book[i]));
    cout << top << endl;
    out.close();
}
Last edited on
Can you please explain why this doesn't work-

Did you read my first post?

You can't use the read and write methods with things like std::string and std::vector. For std::string you need to convert the string to a C-string and you also store the size of the string before the string so you can retrieve it later using read().

For std::vector you need to write each individual element separately.

You can only use the read() and write() methods to read and write POD type (Plain Old Data) like char, int, long, double and any class that are have non-trivial constructors and don't contain pointers.

> Can you please explain why this doesn't work

std::ostream& write( const char* p, std::streamsize n ) outputs n characters from a character array whose first element is pointed to by p.

std::istream& read( char* p, std::streamsize n ) extracts and stores up to n characters into the character array whose first element is pointed to by p.

The read() / write() constructs can also be used for i/o of TriviallyCopyable types.
http://en.cppreference.com/w/cpp/concept/TriviallyCopyable

For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. - IS


std::string and std::vector<> are not TriviallyCopyable types. consequently a class (for instance student) that has non-static member objects of these types is also not TriviallyCopyable types.

In brief: we just can't perform i/o of objects of type student with read() and write().
Last edited on
Thanks to all... It was helpful. Now I understand something.
Last edited on
Topic archived. No new replies allowed.