'How to store class object having string in binary file?
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
Solution 1:[1]
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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | paolo |
