Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store class object having string in binary file?

Tags:

c++

c++11

c++17

I'm storing my class object in the binary file but I'm getting weird results when I load the data.
Following Code is Loading and Saving Data:

#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?

I'm pretty sure the problem is with string but I don't know how to fix it! I have already known to store strings in binary files but don't know how to store class objects which have string in it

like image 937
zain ul din Avatar asked Jan 24 '26 09:01

zain ul din


1 Answers

I would introduce a new level of indirection, i.e. functions from_binary and to_binary, and implement your Load and Store in terms of those:

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

For POD data types, from_binary and to_binary will just do what you already did in Load/Store (beware, however: pointers are PODs but saving an address is pretty much meaningless):

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

As pointed out in the comments, std::string is not a POD type. I'm going to serialize it by saving the character count and then the actual characters:

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

Also, I'm going to serialize/deserialize an array by calling to_binary/from_binary on each element of the array:

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

The above functions are enough to implement from_binary and to_binary for your Contact and Data classes:

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

Output:

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

Although this may solve your particular issue, the number of overloads you'll need for from_binary and to_binary will grow very rapidly as your project grows. So I'd definetely check if there's a more comprehensive (and well tested) library solution out there.

like image 109
paolo Avatar answered Jan 26 '26 21:01

paolo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!