Search code examples
c++serializationbinaryfiles

How to serialize and deserialize an object into/from binary files manually?


I've been trying to write the below object into a file and got lot of trouble since strings are dynamically allocated.

class Student{
    string name, email, telephoneNo;
    int addmissionNo;
    vector<string> issued_books;
public:
    // There are some methods to initialize name, email, etc...
};

So I got to know that I can't just write into a file or read from a file an object with serialization. So I searched all over the internet about serialization with cpp and got to know about Boost library.But I wanted to do it my own (I know writing a library that already exist is not good, but I wanna see what's going on inside the code). So I got to know about overloading iostream << and >>. And I also know that serialize/deserialize into/from text.

But I want to serialize into a binary file. So I tried overloading ostream write and istream read. But then I got size issues(as write and read needs the sizeof the object it writes/reads).Then I also got to know about stringstream can help to serialize/deserialize objects into/from binary. But I don't how to do that?

So my real question is How to serialize and deserialize an object into/from binary files without third party libraries?


Solution

  • I have found a solution serialize and deserialize an object into/from a file. Here is an explaination

    As I told you this is my class. And I have added two functions which overload the iostream's write and read.

    class Student{
        string name, email, telephoneNo;
        int addmissionNo;
        vector<string> issuedBooks;
    public:
        void create();  // initialize the private members
        void show();  // showing details
    
        // and some other functions as well...
        
        // here I'm overloading the iostream's write and read
        friend ostream& write(ostream& out, Student& obj);
        friend istream& read(istream& in, Student& obj);
    };
    

    But I have also told you that I have tried this already. The problem I have was how to read without object member's size. So I made changes as below (Please read comments also).

    // write: overload the standard library write function and return an ostream
    // @param out: an ostream
    // @param obj: a Student object
    ostream& write(ostream& out, Student& obj){
        // writing the objet's members one by one.
        out.write(obj.name.c_str(), obj.name.length() + 1); // +1 for the terminating '\0'
        out.write(obj.email.c_str(), obj.email.length() + 1);
        out.write(obj.telephoneNo.c_str(), obj.telephoneNo.length() + 1);
        out.write((char*)&obj.addmissionNo, sizeof(obj.addmissionNo)); // int are just cast into     a char* and write into the object's member
     
        // writing the vector of issued books   
        for (string& book: obj.issuedBooks){
            out.write(book.c_str(), book.length() + 1);
        }
        return out;
    }
     
    // read: overload the standard library read function and return an istream
    // @param in: an istream
    // @param obj: a Student object
    istream& read(istream& in, Student& obj){
        // getline is used rather than read
        // since getline reads a whole line and can be give a delim character
        getline(in, obj.name, '\0');  // delimiting character is '\0' 
        getline(in, obj.email, '\0');
        getline(in, obj.telephoneNo, '\0');
        in.read((char*)&obj.addmissionNo, sizeof(int));
    
        for (string& book: obj.issuedBooks){
            getline(in, book, '\0');
        }
                return in;
    }
    

    As you can see I have wrote length+1 for the terminating '\0'. It is usefull in read function as we have used getline instead of read. So getline reads until the '\0'. So no need of a size. And here I'm writing and reading into/from a file.

    void writeStudent(Student s, ofstream& f){
        char ch; // flag for the loop
        do{
            s.create(); // making a student        
            f.open("students", ios::app | ios::binary); // the file to be written
            write(f, s); // the overloaded function
            f.close();
    
            cout << "Do you want to add another record? (y/n): ";
            cin >> ch;
            cin.ignore();
        } while(toupper(ch) == 'Y'); // loop until user stop adding records. 
    }
    
    void readStudent(Student s, ifstream& f){
        char ch; // flag for the loop
        do{
            f.open("students", ios::in | ios::binary);
     
            cout << "Enter the account no of the student: ";
            int no;
            cin >> no;
    
            int found = 0;
            while (read(f, s)){
                if (s.retAddmissionNo() == no){
                    found = 1;
                    s.show();
                }
            }
            if (!found)
                cout << "Account Not found!\n";
     
            f.close();
            cout << "Do you want another record? (y/n): ";
            cin >> ch;
        } while(toupper(ch) == 'Y');
    }
    

    That's how I solved my problem. If something wrong here please comment. Thank you!