如何在二进制文件中存储 class 具有字符串的对象?

How to store class object having string in binary file?

我正在将我的 class 对象存储在二进制文件中,但是当我加载数据时我得到了奇怪的结果。
以下代码正在加载和保存数据:

#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <sstream>
using namespace std;

template <class C>
void Load(const char fileName[], C& obj)
{
    ifstream in;
    in.open(fileName, ios::in | ios::binary);
    in.read(reinterpret_cast<char*>(addressof(obj)), sizeof(obj));
    in.close();
    return;
}

template <class T>
void Save(const char fileName[], T obj)
{
    ofstream out;
    out.open(fileName, ios::out | ios::binary);
    out.write(reinterpret_cast<char const*>(addressof(obj)), sizeof(obj));
    stringstream ss;
    out.close();
    return;
}

class Contact {
public:
    int CompareTo(Contact obj)
    {
        return 1;
    }
    string ss;
    int rollNum;
};

class Data {
public:
    Data()
    {
    }
    Contact arr[10];
};

int main()
{
    const char fileName[] = "ContactMG.dat";
    /*
     Data *T = new Data();
    
     for(int i=0;i<10;i++)
          T->arr[i].ss = "fahad";
       Save(fileName , *T);
    */

    Data* d = new Data();
    Load(fileName, *d);
    for (int i = 0; i < 10; i++)
        cout << d->arr[i].ss << endl;
}

/*
 Console outPut:
ⁿx

 p²x
   σß╥Z∙
  ░▒▓│┤
   >
☺Y╩
░‼╩

*/

/* Binary File
   @®     ®     ®     
*/

I want to ask how I can store this object in the binary file and load it?

我很确定问题出在字符串上,但我不知道如何解决! 我已经知道将字符串存储在二进制文件中,但不知道如何存储 class 中包含字符串的对象

我会引入一个新的间接级别,即函数 from_binaryto_binary,并根据这些实现您的 LoadStore

template <class C>
bool Load(const char fileName[], C& obj) {
    if (ifstream in{fileName, ios::in | ios::binary}) {
        from_binary(in, obj);
        return true;
    }

    return false;
}

template <class T>
bool Save(const char fileName[], T obj) {
    if (ofstream out{fileName, ios::out | ios::binary}) {
        to_binary(out, obj);
        return true;
    }

    return false;
}

对于 POD 数据类型,from_binaryto_binary 将执行您在 Load/Store 中已经执行的操作(但是请注意:指针是 PODs 但保存地址几乎没有意义):

template <class T, typename = enable_if_t<is_pod_v<T>>>
void from_binary(ifstream& in, T& obj) {
    in.read(reinterpret_cast<char*>(addressof(obj)), sizeof(obj));
}

template <class T, typename = enable_if_t<is_pod_v<T>>>
void to_binary(ofstream& out, T const& obj) {
    out.write(reinterpret_cast<char const*>(addressof(obj)), sizeof(obj));
}

正如评论中指出的那样,std::string 不是 POD 类型。我将通过保存字符数然后保存实际字符来序列化它:

void from_binary(ifstream& in, string& str) {
    std::size_t stringSize{0};
    from_binary(in, stringSize);

    str.reserve(stringSize);
    for (size_t i = 0; i != stringSize; ++i) {
        char ch{};
        in.read(&ch, 1);
        str.push_back(ch);
    }
}

void to_binary(ofstream& out, string const& str) {
    auto const stringSize = str.size();
    to_binary(out, stringSize);

    auto const* cStr = str.c_str();
    out.write(cStr, stringSize);
}

此外,我将通过对数组的每个元素调用 to_binary/from_binary 来 serialize/deserialize 一个数组:

template <class T, size_t N>
void from_binary(ifstream& in, T (&obj)[N]) {
    for (auto& elem : obj) from_binary(in, elem);
}

template <class T, size_t N>
void to_binary(ofstream& out, T const (&obj)[N]) {
    for (auto const& elem : obj) to_binary(out, elem);
}

上面的函数足以实现你的ContactDatafrom_binaryto_binary 类:

#include <fstream>
#include <iostream>
#include <string>

using namespace std;

template <class T, typename = enable_if_t<is_pod_v<T>>>
void from_binary(ifstream& in, T& obj) {
    in.read(reinterpret_cast<char*>(addressof(obj)), sizeof(obj));
}

template <class T, typename = enable_if_t<is_pod_v<T>>>
void to_binary(ofstream& out, T const& obj) {
    out.write(reinterpret_cast<char const*>(addressof(obj)), sizeof(obj));
}

void from_binary(ifstream& in, string& str) {
    std::size_t stringSize{0};
    from_binary(in, stringSize);

    str.reserve(stringSize);
    for (size_t i = 0; i != stringSize; ++i) {
        char ch{};
        in.read(&ch, 1);
        str.push_back(ch);
    }
}

void to_binary(ofstream& out, string const& str) {
    auto const stringSize = str.size();
    to_binary(out, stringSize);

    auto const* cStr = str.c_str();
    out.write(cStr, stringSize);
}

template <class T, size_t N>
void from_binary(ifstream& in, T (&obj)[N]) {
    for (auto& elem : obj) from_binary(in, elem);
}

template <class T, size_t N>
void to_binary(ofstream& out, T const (&obj)[N]) {
    for (auto const& elem : obj) to_binary(out, elem);
}

template <class C>
bool Load(const char fileName[], C& obj) {
    if (ifstream in{fileName, ios::in | ios::binary}) {
        from_binary(in, obj);
        return true;
    }

    return false;
}

template <class T>
bool Save(const char fileName[], T obj) {
    if (ofstream out{fileName, ios::out | ios::binary}) {
        to_binary(out, obj);
        return true;
    }

    return false;
}

class Contact {
   public:
    int CompareTo(Contact obj) { return 1; }
    string ss;
    int rollNum;
};

void from_binary(ifstream& in, Contact& obj) {
    from_binary(in, obj.ss);
    from_binary(in, obj.rollNum);
}

void to_binary(ofstream& out, Contact const& obj) {
    to_binary(out, obj.ss);
    to_binary(out, obj.rollNum);
}

class Data {
   public:
    Data() {}
    Contact arr[10];
};

void from_binary(ifstream& in, Data& obj) { from_binary(in, obj.arr); }

void to_binary(ofstream& out, Data const& obj) { to_binary(out, obj.arr); }

int main() {
    const char fileName[] = "ContactMG.dat";

    {
        Data data;

        auto const contactCount = sizeof(data.arr) / sizeof(data.arr[0]);
        for (size_t c = 0; c != contactCount; ++c) {
            data.arr[c].ss = "some name " + to_string(c);
            data.arr[c].rollNum = c;
        }

        Save(fileName, data);
    }

    {
        Data data;
        Load(fileName, data);

        for (auto const& contact : data.arr)
            cout << "Contact: rollNum=" << contact.rollNum
                 << ", ss=" << contact.ss << '\n';
    }
}

输出:

Contact: rollNum=0, ss=some name 0
Contact: rollNum=1, ss=some name 1
Contact: rollNum=2, ss=some name 2
Contact: rollNum=3, ss=some name 3
Contact: rollNum=4, ss=some name 4
Contact: rollNum=5, ss=some name 5
Contact: rollNum=6, ss=some name 6
Contact: rollNum=7, ss=some name 7
Contact: rollNum=8, ss=some name 8
Contact: rollNum=9, ss=some name 9

虽然这可能会解决您的特定问题,但随着项目的增长,from_binaryto_binary 所需的重载数量会迅速增加。所以我肯定会检查是否有更全面(且经过良好测试)的库解决方案。