如何手动序列化和反序列化对象 into/from 二进制文件?

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

我一直在尝试将以下对象写入文件,但遇到了很多麻烦,因为字符串是动态分配的。

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

所以我知道我不能只写入文件或从文件中读取具有 serialization 的对象。所以我在互联网上搜索了有关使用 cpp 进行序列化的信息,并了解了 Boost library.But 我想自己做(我知道编写一个已经存在的库不好,但我想看看代码内部发生了什么)。所以我开始了解重载 iostream <<>>。而且我也知道 serialize/deserialize into/from text.

但是我想序列化成一个二进制文件。所以我尝试重载 ostream write 和 istream read。但是后来我遇到了大小问题(因为写入和读取需要对象的大小 writes/reads)。然后我还了解到 stringstream 可以帮助 serialize/deserialize 对象 into/from二进制。但我不知道该怎么做?

所以我真正的问题是如何在没有第三方库的情况下序列化和反序列化对象 into/from 二进制文件?

我找到了序列化和反序列化对象 into/from 文件的解决方案。这里有一个解释

正如我告诉过你的,这是我的 class。并且我添加了两个使 iostream 的写入和读取过载的函数。

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

但是我也告诉过你我已经试过了。我遇到的问题是如何在没有对象成员大小的情况下进行阅读。所以我做了如下更改(也请阅读评论)。

// 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 '[=11=]'
    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, '[=11=]');  // delimiting character is '[=11=]' 
    getline(in, obj.email, '[=11=]');
    getline(in, obj.telephoneNo, '[=11=]');
    in.read((char*)&obj.addmissionNo, sizeof(int));

    for (string& book: obj.issuedBooks){
        getline(in, book, '[=11=]');
    }
            return in;
}

如您所见,我为终止 '[=14=]' 写了 length+1。它在读取函数中很有用,因为我们使用了 getline 而不是读取。所以 getline 一直读取到 '[=14=]'。所以不需要尺寸。在这里,我正在写入和读取 into/from 一个文件。

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

这就是我解决问题的方法。如果这里有什么问题请评论。谢谢!