This is a question about reading a binary file [duplicate] - c++

I'm not a c++ expert but I've serialized things a couple of times in the past. Unfortunately this time I'm trying to serialize a class which contains an std::string, which I understand is pretty much like serializing a pointer.
I can write out the class to a file and read it back in again. All int fields are fine, but the std::string field gives an "address out of bounds" error, presumably because it points to data which is no longer there.
Is there a standard workaround for this? I don't want to go back to char arrays, but at least I know they work in this situation. I can provide code if necessary, but I'm hoping I've explained my problem well.
I'm serializing by casting the class to a char* and writing it to a file with std::fstream. Reading of course is just the reverse.

I'm serializing by casting the class to a char* and writing it to a
file with fstream. Reading of course is just the reverse.
Unfortunately, this only works as long as there are no pointers involved. You might want to give your classes void MyClass::serialize(std::ostream) and void MyClass::deserialize(std::ifstream), and call those. For this case, you'd want
std::ostream& MyClass::serialize(std::ostream &out) const {
out << height;
out << ',' //number seperator
out << width;
out << ',' //number seperator
out << name.size(); //serialize size of string
out << ',' //number seperator
out << name; //serialize characters of string
return out;
}
std::istream& MyClass::deserialize(std::istream &in) {
if (in) {
int len=0;
char comma;
in >> height;
in >> comma; //read in the seperator
in >> width;
in >> comma; //read in the seperator
in >> len; //deserialize size of string
in >> comma; //read in the seperator
if (in && len) {
std::vector<char> tmp(len);
in.read(tmp.data() , len); //deserialize characters of string
name.assign(tmp.data(), len);
}
}
return in;
}
You may also want to overload the stream operators for easier use.
std::ostream &operator<<(std::ostream& out, const MyClass &obj)
{obj.serialize(out); return out;}
std::istream &operator>>(std::istream& in, MyClass &obj)
{obj.deserialize(in); return in;}

Simply writing the binary contents of an object into a file is not only unportable but, as you've recognized, doesn't work for pointer data. You basically have two options: either you write a real serialization library, which handles std::strings properly by e.g. using c_str() to output the actual string to the file, or you use the excellent boost serialization library. If at all possible, I'd recommend the latter, you can then serialize with a simple code like this:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
class A {
private:
std::string s;
public:
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & s;
}
};
Here, the function serialize works for serializing and deserializing the data, depending on how you call it. See the documentation for more information.

The easiest serialization method for strings or other blobs with variable size is to serialize first the size as you serialize integers, then just copy the content to the output stream.
When reading you first read the size, then allocate the string and then fill it by reading the correct number of bytes from the stream.
An alternative is to use a delimiter and escaping, but requires more code and is slower both on serialization and deserialization (however the result can be kept human readable).

You'll have to use a more complicated method of serialization than casting a class to a char* and writing it to a file if your class contains any exogenous data (string does). And you're correct about why you're getting a segmentation fault.
I would make a member function that would take an fstream and read in the data from it as well as an inverse function which would take an fstream and write it's contents to it to be restored later, like this:
class MyClass {
pubic:
MyClass() : str() { }
void serialize(ostream& out) {
out << str;
}
void restore(istream& in) {
in >> str;
}
string& data() const { return str; }
private:
string str;
};
MyClass c;
c.serialize(output);
// later
c.restore(input);
You can also define operator<< and operator>> to work with istream and ostream to serialize and restore your class as well if you want that syntactic sugar.

Why not just something along the lines of:
std::ofstream ofs;
...
ofs << my_str;
and then:
std::ifstream ifs;
...
ifs >> my_str;

/*!
* reads binary data into the string.
* #status : OK.
*/
class UReadBinaryString
{
static std::string read(std::istream &is, uint32_t size)
{
std::string returnStr;
if(size > 0)
{
CWrapPtr<char> buff(new char[size]); // custom smart pointer
is.read(reinterpret_cast<char*>(buff.m_obj), size);
returnStr.assign(buff.m_obj, size);
}
return returnStr;
}
};
class objHeader
{
public:
std::string m_ID;
// serialize
std::ostream &operator << (std::ostream &os)
{
uint32_t size = (m_ID.length());
os.write(reinterpret_cast<char*>(&size), sizeof(uint32_t));
os.write(m_ID.c_str(), size);
return os;
}
// de-serialize
std::istream &operator >> (std::istream &is)
{
uint32_t size;
is.read(reinterpret_cast<char*>(&size), sizeof(uint32_t));
m_ID = UReadBinaryString::read(is, size);
return is;
}
};

I haven't coded C++ in a long time, but perhaps you could serialize an array of char.
Then, when you open your file, your string would just point to the array.
Just an idea.

Related

Problems with binary files, incomplete information c++ [duplicate]

I'm not a c++ expert but I've serialized things a couple of times in the past. Unfortunately this time I'm trying to serialize a class which contains an std::string, which I understand is pretty much like serializing a pointer.
I can write out the class to a file and read it back in again. All int fields are fine, but the std::string field gives an "address out of bounds" error, presumably because it points to data which is no longer there.
Is there a standard workaround for this? I don't want to go back to char arrays, but at least I know they work in this situation. I can provide code if necessary, but I'm hoping I've explained my problem well.
I'm serializing by casting the class to a char* and writing it to a file with std::fstream. Reading of course is just the reverse.
I'm serializing by casting the class to a char* and writing it to a
file with fstream. Reading of course is just the reverse.
Unfortunately, this only works as long as there are no pointers involved. You might want to give your classes void MyClass::serialize(std::ostream) and void MyClass::deserialize(std::ifstream), and call those. For this case, you'd want
std::ostream& MyClass::serialize(std::ostream &out) const {
out << height;
out << ',' //number seperator
out << width;
out << ',' //number seperator
out << name.size(); //serialize size of string
out << ',' //number seperator
out << name; //serialize characters of string
return out;
}
std::istream& MyClass::deserialize(std::istream &in) {
if (in) {
int len=0;
char comma;
in >> height;
in >> comma; //read in the seperator
in >> width;
in >> comma; //read in the seperator
in >> len; //deserialize size of string
in >> comma; //read in the seperator
if (in && len) {
std::vector<char> tmp(len);
in.read(tmp.data() , len); //deserialize characters of string
name.assign(tmp.data(), len);
}
}
return in;
}
You may also want to overload the stream operators for easier use.
std::ostream &operator<<(std::ostream& out, const MyClass &obj)
{obj.serialize(out); return out;}
std::istream &operator>>(std::istream& in, MyClass &obj)
{obj.deserialize(in); return in;}
Simply writing the binary contents of an object into a file is not only unportable but, as you've recognized, doesn't work for pointer data. You basically have two options: either you write a real serialization library, which handles std::strings properly by e.g. using c_str() to output the actual string to the file, or you use the excellent boost serialization library. If at all possible, I'd recommend the latter, you can then serialize with a simple code like this:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
class A {
private:
std::string s;
public:
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & s;
}
};
Here, the function serialize works for serializing and deserializing the data, depending on how you call it. See the documentation for more information.
The easiest serialization method for strings or other blobs with variable size is to serialize first the size as you serialize integers, then just copy the content to the output stream.
When reading you first read the size, then allocate the string and then fill it by reading the correct number of bytes from the stream.
An alternative is to use a delimiter and escaping, but requires more code and is slower both on serialization and deserialization (however the result can be kept human readable).
You'll have to use a more complicated method of serialization than casting a class to a char* and writing it to a file if your class contains any exogenous data (string does). And you're correct about why you're getting a segmentation fault.
I would make a member function that would take an fstream and read in the data from it as well as an inverse function which would take an fstream and write it's contents to it to be restored later, like this:
class MyClass {
pubic:
MyClass() : str() { }
void serialize(ostream& out) {
out << str;
}
void restore(istream& in) {
in >> str;
}
string& data() const { return str; }
private:
string str;
};
MyClass c;
c.serialize(output);
// later
c.restore(input);
You can also define operator<< and operator>> to work with istream and ostream to serialize and restore your class as well if you want that syntactic sugar.
Why not just something along the lines of:
std::ofstream ofs;
...
ofs << my_str;
and then:
std::ifstream ifs;
...
ifs >> my_str;
/*!
* reads binary data into the string.
* #status : OK.
*/
class UReadBinaryString
{
static std::string read(std::istream &is, uint32_t size)
{
std::string returnStr;
if(size > 0)
{
CWrapPtr<char> buff(new char[size]); // custom smart pointer
is.read(reinterpret_cast<char*>(buff.m_obj), size);
returnStr.assign(buff.m_obj, size);
}
return returnStr;
}
};
class objHeader
{
public:
std::string m_ID;
// serialize
std::ostream &operator << (std::ostream &os)
{
uint32_t size = (m_ID.length());
os.write(reinterpret_cast<char*>(&size), sizeof(uint32_t));
os.write(m_ID.c_str(), size);
return os;
}
// de-serialize
std::istream &operator >> (std::istream &is)
{
uint32_t size;
is.read(reinterpret_cast<char*>(&size), sizeof(uint32_t));
m_ID = UReadBinaryString::read(is, size);
return is;
}
};
I haven't coded C++ in a long time, but perhaps you could serialize an array of char.
Then, when you open your file, your string would just point to the array.
Just an idea.

Need help tracking down an access error violation [duplicate]

I'm not a c++ expert but I've serialized things a couple of times in the past. Unfortunately this time I'm trying to serialize a class which contains an std::string, which I understand is pretty much like serializing a pointer.
I can write out the class to a file and read it back in again. All int fields are fine, but the std::string field gives an "address out of bounds" error, presumably because it points to data which is no longer there.
Is there a standard workaround for this? I don't want to go back to char arrays, but at least I know they work in this situation. I can provide code if necessary, but I'm hoping I've explained my problem well.
I'm serializing by casting the class to a char* and writing it to a file with std::fstream. Reading of course is just the reverse.
I'm serializing by casting the class to a char* and writing it to a
file with fstream. Reading of course is just the reverse.
Unfortunately, this only works as long as there are no pointers involved. You might want to give your classes void MyClass::serialize(std::ostream) and void MyClass::deserialize(std::ifstream), and call those. For this case, you'd want
std::ostream& MyClass::serialize(std::ostream &out) const {
out << height;
out << ',' //number seperator
out << width;
out << ',' //number seperator
out << name.size(); //serialize size of string
out << ',' //number seperator
out << name; //serialize characters of string
return out;
}
std::istream& MyClass::deserialize(std::istream &in) {
if (in) {
int len=0;
char comma;
in >> height;
in >> comma; //read in the seperator
in >> width;
in >> comma; //read in the seperator
in >> len; //deserialize size of string
in >> comma; //read in the seperator
if (in && len) {
std::vector<char> tmp(len);
in.read(tmp.data() , len); //deserialize characters of string
name.assign(tmp.data(), len);
}
}
return in;
}
You may also want to overload the stream operators for easier use.
std::ostream &operator<<(std::ostream& out, const MyClass &obj)
{obj.serialize(out); return out;}
std::istream &operator>>(std::istream& in, MyClass &obj)
{obj.deserialize(in); return in;}
Simply writing the binary contents of an object into a file is not only unportable but, as you've recognized, doesn't work for pointer data. You basically have two options: either you write a real serialization library, which handles std::strings properly by e.g. using c_str() to output the actual string to the file, or you use the excellent boost serialization library. If at all possible, I'd recommend the latter, you can then serialize with a simple code like this:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
class A {
private:
std::string s;
public:
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & s;
}
};
Here, the function serialize works for serializing and deserializing the data, depending on how you call it. See the documentation for more information.
The easiest serialization method for strings or other blobs with variable size is to serialize first the size as you serialize integers, then just copy the content to the output stream.
When reading you first read the size, then allocate the string and then fill it by reading the correct number of bytes from the stream.
An alternative is to use a delimiter and escaping, but requires more code and is slower both on serialization and deserialization (however the result can be kept human readable).
You'll have to use a more complicated method of serialization than casting a class to a char* and writing it to a file if your class contains any exogenous data (string does). And you're correct about why you're getting a segmentation fault.
I would make a member function that would take an fstream and read in the data from it as well as an inverse function which would take an fstream and write it's contents to it to be restored later, like this:
class MyClass {
pubic:
MyClass() : str() { }
void serialize(ostream& out) {
out << str;
}
void restore(istream& in) {
in >> str;
}
string& data() const { return str; }
private:
string str;
};
MyClass c;
c.serialize(output);
// later
c.restore(input);
You can also define operator<< and operator>> to work with istream and ostream to serialize and restore your class as well if you want that syntactic sugar.
Why not just something along the lines of:
std::ofstream ofs;
...
ofs << my_str;
and then:
std::ifstream ifs;
...
ifs >> my_str;
/*!
* reads binary data into the string.
* #status : OK.
*/
class UReadBinaryString
{
static std::string read(std::istream &is, uint32_t size)
{
std::string returnStr;
if(size > 0)
{
CWrapPtr<char> buff(new char[size]); // custom smart pointer
is.read(reinterpret_cast<char*>(buff.m_obj), size);
returnStr.assign(buff.m_obj, size);
}
return returnStr;
}
};
class objHeader
{
public:
std::string m_ID;
// serialize
std::ostream &operator << (std::ostream &os)
{
uint32_t size = (m_ID.length());
os.write(reinterpret_cast<char*>(&size), sizeof(uint32_t));
os.write(m_ID.c_str(), size);
return os;
}
// de-serialize
std::istream &operator >> (std::istream &is)
{
uint32_t size;
is.read(reinterpret_cast<char*>(&size), sizeof(uint32_t));
m_ID = UReadBinaryString::read(is, size);
return is;
}
};
I haven't coded C++ in a long time, but perhaps you could serialize an array of char.
Then, when you open your file, your string would just point to the array.
Just an idea.

How to properly read from a txt file into a struct [duplicate]

I'm not a c++ expert but I've serialized things a couple of times in the past. Unfortunately this time I'm trying to serialize a class which contains an std::string, which I understand is pretty much like serializing a pointer.
I can write out the class to a file and read it back in again. All int fields are fine, but the std::string field gives an "address out of bounds" error, presumably because it points to data which is no longer there.
Is there a standard workaround for this? I don't want to go back to char arrays, but at least I know they work in this situation. I can provide code if necessary, but I'm hoping I've explained my problem well.
I'm serializing by casting the class to a char* and writing it to a file with std::fstream. Reading of course is just the reverse.
I'm serializing by casting the class to a char* and writing it to a
file with fstream. Reading of course is just the reverse.
Unfortunately, this only works as long as there are no pointers involved. You might want to give your classes void MyClass::serialize(std::ostream) and void MyClass::deserialize(std::ifstream), and call those. For this case, you'd want
std::ostream& MyClass::serialize(std::ostream &out) const {
out << height;
out << ',' //number seperator
out << width;
out << ',' //number seperator
out << name.size(); //serialize size of string
out << ',' //number seperator
out << name; //serialize characters of string
return out;
}
std::istream& MyClass::deserialize(std::istream &in) {
if (in) {
int len=0;
char comma;
in >> height;
in >> comma; //read in the seperator
in >> width;
in >> comma; //read in the seperator
in >> len; //deserialize size of string
in >> comma; //read in the seperator
if (in && len) {
std::vector<char> tmp(len);
in.read(tmp.data() , len); //deserialize characters of string
name.assign(tmp.data(), len);
}
}
return in;
}
You may also want to overload the stream operators for easier use.
std::ostream &operator<<(std::ostream& out, const MyClass &obj)
{obj.serialize(out); return out;}
std::istream &operator>>(std::istream& in, MyClass &obj)
{obj.deserialize(in); return in;}
Simply writing the binary contents of an object into a file is not only unportable but, as you've recognized, doesn't work for pointer data. You basically have two options: either you write a real serialization library, which handles std::strings properly by e.g. using c_str() to output the actual string to the file, or you use the excellent boost serialization library. If at all possible, I'd recommend the latter, you can then serialize with a simple code like this:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
class A {
private:
std::string s;
public:
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & s;
}
};
Here, the function serialize works for serializing and deserializing the data, depending on how you call it. See the documentation for more information.
The easiest serialization method for strings or other blobs with variable size is to serialize first the size as you serialize integers, then just copy the content to the output stream.
When reading you first read the size, then allocate the string and then fill it by reading the correct number of bytes from the stream.
An alternative is to use a delimiter and escaping, but requires more code and is slower both on serialization and deserialization (however the result can be kept human readable).
You'll have to use a more complicated method of serialization than casting a class to a char* and writing it to a file if your class contains any exogenous data (string does). And you're correct about why you're getting a segmentation fault.
I would make a member function that would take an fstream and read in the data from it as well as an inverse function which would take an fstream and write it's contents to it to be restored later, like this:
class MyClass {
pubic:
MyClass() : str() { }
void serialize(ostream& out) {
out << str;
}
void restore(istream& in) {
in >> str;
}
string& data() const { return str; }
private:
string str;
};
MyClass c;
c.serialize(output);
// later
c.restore(input);
You can also define operator<< and operator>> to work with istream and ostream to serialize and restore your class as well if you want that syntactic sugar.
Why not just something along the lines of:
std::ofstream ofs;
...
ofs << my_str;
and then:
std::ifstream ifs;
...
ifs >> my_str;
/*!
* reads binary data into the string.
* #status : OK.
*/
class UReadBinaryString
{
static std::string read(std::istream &is, uint32_t size)
{
std::string returnStr;
if(size > 0)
{
CWrapPtr<char> buff(new char[size]); // custom smart pointer
is.read(reinterpret_cast<char*>(buff.m_obj), size);
returnStr.assign(buff.m_obj, size);
}
return returnStr;
}
};
class objHeader
{
public:
std::string m_ID;
// serialize
std::ostream &operator << (std::ostream &os)
{
uint32_t size = (m_ID.length());
os.write(reinterpret_cast<char*>(&size), sizeof(uint32_t));
os.write(m_ID.c_str(), size);
return os;
}
// de-serialize
std::istream &operator >> (std::istream &is)
{
uint32_t size;
is.read(reinterpret_cast<char*>(&size), sizeof(uint32_t));
m_ID = UReadBinaryString::read(is, size);
return is;
}
};
I haven't coded C++ in a long time, but perhaps you could serialize an array of char.
Then, when you open your file, your string would just point to the array.
Just an idea.

Save vector of structures on binary file [duplicate]

I'm not a c++ expert but I've serialized things a couple of times in the past. Unfortunately this time I'm trying to serialize a class which contains an std::string, which I understand is pretty much like serializing a pointer.
I can write out the class to a file and read it back in again. All int fields are fine, but the std::string field gives an "address out of bounds" error, presumably because it points to data which is no longer there.
Is there a standard workaround for this? I don't want to go back to char arrays, but at least I know they work in this situation. I can provide code if necessary, but I'm hoping I've explained my problem well.
I'm serializing by casting the class to a char* and writing it to a file with std::fstream. Reading of course is just the reverse.
I'm serializing by casting the class to a char* and writing it to a
file with fstream. Reading of course is just the reverse.
Unfortunately, this only works as long as there are no pointers involved. You might want to give your classes void MyClass::serialize(std::ostream) and void MyClass::deserialize(std::ifstream), and call those. For this case, you'd want
std::ostream& MyClass::serialize(std::ostream &out) const {
out << height;
out << ',' //number seperator
out << width;
out << ',' //number seperator
out << name.size(); //serialize size of string
out << ',' //number seperator
out << name; //serialize characters of string
return out;
}
std::istream& MyClass::deserialize(std::istream &in) {
if (in) {
int len=0;
char comma;
in >> height;
in >> comma; //read in the seperator
in >> width;
in >> comma; //read in the seperator
in >> len; //deserialize size of string
in >> comma; //read in the seperator
if (in && len) {
std::vector<char> tmp(len);
in.read(tmp.data() , len); //deserialize characters of string
name.assign(tmp.data(), len);
}
}
return in;
}
You may also want to overload the stream operators for easier use.
std::ostream &operator<<(std::ostream& out, const MyClass &obj)
{obj.serialize(out); return out;}
std::istream &operator>>(std::istream& in, MyClass &obj)
{obj.deserialize(in); return in;}
Simply writing the binary contents of an object into a file is not only unportable but, as you've recognized, doesn't work for pointer data. You basically have two options: either you write a real serialization library, which handles std::strings properly by e.g. using c_str() to output the actual string to the file, or you use the excellent boost serialization library. If at all possible, I'd recommend the latter, you can then serialize with a simple code like this:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
class A {
private:
std::string s;
public:
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & s;
}
};
Here, the function serialize works for serializing and deserializing the data, depending on how you call it. See the documentation for more information.
The easiest serialization method for strings or other blobs with variable size is to serialize first the size as you serialize integers, then just copy the content to the output stream.
When reading you first read the size, then allocate the string and then fill it by reading the correct number of bytes from the stream.
An alternative is to use a delimiter and escaping, but requires more code and is slower both on serialization and deserialization (however the result can be kept human readable).
You'll have to use a more complicated method of serialization than casting a class to a char* and writing it to a file if your class contains any exogenous data (string does). And you're correct about why you're getting a segmentation fault.
I would make a member function that would take an fstream and read in the data from it as well as an inverse function which would take an fstream and write it's contents to it to be restored later, like this:
class MyClass {
pubic:
MyClass() : str() { }
void serialize(ostream& out) {
out << str;
}
void restore(istream& in) {
in >> str;
}
string& data() const { return str; }
private:
string str;
};
MyClass c;
c.serialize(output);
// later
c.restore(input);
You can also define operator<< and operator>> to work with istream and ostream to serialize and restore your class as well if you want that syntactic sugar.
Why not just something along the lines of:
std::ofstream ofs;
...
ofs << my_str;
and then:
std::ifstream ifs;
...
ifs >> my_str;
/*!
* reads binary data into the string.
* #status : OK.
*/
class UReadBinaryString
{
static std::string read(std::istream &is, uint32_t size)
{
std::string returnStr;
if(size > 0)
{
CWrapPtr<char> buff(new char[size]); // custom smart pointer
is.read(reinterpret_cast<char*>(buff.m_obj), size);
returnStr.assign(buff.m_obj, size);
}
return returnStr;
}
};
class objHeader
{
public:
std::string m_ID;
// serialize
std::ostream &operator << (std::ostream &os)
{
uint32_t size = (m_ID.length());
os.write(reinterpret_cast<char*>(&size), sizeof(uint32_t));
os.write(m_ID.c_str(), size);
return os;
}
// de-serialize
std::istream &operator >> (std::istream &is)
{
uint32_t size;
is.read(reinterpret_cast<char*>(&size), sizeof(uint32_t));
m_ID = UReadBinaryString::read(is, size);
return is;
}
};
I haven't coded C++ in a long time, but perhaps you could serialize an array of char.
Then, when you open your file, your string would just point to the array.
Just an idea.

Serializing a class which contains a std::string

I'm not a c++ expert but I've serialized things a couple of times in the past. Unfortunately this time I'm trying to serialize a class which contains an std::string, which I understand is pretty much like serializing a pointer.
I can write out the class to a file and read it back in again. All int fields are fine, but the std::string field gives an "address out of bounds" error, presumably because it points to data which is no longer there.
Is there a standard workaround for this? I don't want to go back to char arrays, but at least I know they work in this situation. I can provide code if necessary, but I'm hoping I've explained my problem well.
I'm serializing by casting the class to a char* and writing it to a file with std::fstream. Reading of course is just the reverse.
I'm serializing by casting the class to a char* and writing it to a
file with fstream. Reading of course is just the reverse.
Unfortunately, this only works as long as there are no pointers involved. You might want to give your classes void MyClass::serialize(std::ostream) and void MyClass::deserialize(std::ifstream), and call those. For this case, you'd want
std::ostream& MyClass::serialize(std::ostream &out) const {
out << height;
out << ',' //number seperator
out << width;
out << ',' //number seperator
out << name.size(); //serialize size of string
out << ',' //number seperator
out << name; //serialize characters of string
return out;
}
std::istream& MyClass::deserialize(std::istream &in) {
if (in) {
int len=0;
char comma;
in >> height;
in >> comma; //read in the seperator
in >> width;
in >> comma; //read in the seperator
in >> len; //deserialize size of string
in >> comma; //read in the seperator
if (in && len) {
std::vector<char> tmp(len);
in.read(tmp.data() , len); //deserialize characters of string
name.assign(tmp.data(), len);
}
}
return in;
}
You may also want to overload the stream operators for easier use.
std::ostream &operator<<(std::ostream& out, const MyClass &obj)
{obj.serialize(out); return out;}
std::istream &operator>>(std::istream& in, MyClass &obj)
{obj.deserialize(in); return in;}
Simply writing the binary contents of an object into a file is not only unportable but, as you've recognized, doesn't work for pointer data. You basically have two options: either you write a real serialization library, which handles std::strings properly by e.g. using c_str() to output the actual string to the file, or you use the excellent boost serialization library. If at all possible, I'd recommend the latter, you can then serialize with a simple code like this:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
class A {
private:
std::string s;
public:
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & s;
}
};
Here, the function serialize works for serializing and deserializing the data, depending on how you call it. See the documentation for more information.
The easiest serialization method for strings or other blobs with variable size is to serialize first the size as you serialize integers, then just copy the content to the output stream.
When reading you first read the size, then allocate the string and then fill it by reading the correct number of bytes from the stream.
An alternative is to use a delimiter and escaping, but requires more code and is slower both on serialization and deserialization (however the result can be kept human readable).
You'll have to use a more complicated method of serialization than casting a class to a char* and writing it to a file if your class contains any exogenous data (string does). And you're correct about why you're getting a segmentation fault.
I would make a member function that would take an fstream and read in the data from it as well as an inverse function which would take an fstream and write it's contents to it to be restored later, like this:
class MyClass {
pubic:
MyClass() : str() { }
void serialize(ostream& out) {
out << str;
}
void restore(istream& in) {
in >> str;
}
string& data() const { return str; }
private:
string str;
};
MyClass c;
c.serialize(output);
// later
c.restore(input);
You can also define operator<< and operator>> to work with istream and ostream to serialize and restore your class as well if you want that syntactic sugar.
Why not just something along the lines of:
std::ofstream ofs;
...
ofs << my_str;
and then:
std::ifstream ifs;
...
ifs >> my_str;
/*!
* reads binary data into the string.
* #status : OK.
*/
class UReadBinaryString
{
static std::string read(std::istream &is, uint32_t size)
{
std::string returnStr;
if(size > 0)
{
CWrapPtr<char> buff(new char[size]); // custom smart pointer
is.read(reinterpret_cast<char*>(buff.m_obj), size);
returnStr.assign(buff.m_obj, size);
}
return returnStr;
}
};
class objHeader
{
public:
std::string m_ID;
// serialize
std::ostream &operator << (std::ostream &os)
{
uint32_t size = (m_ID.length());
os.write(reinterpret_cast<char*>(&size), sizeof(uint32_t));
os.write(m_ID.c_str(), size);
return os;
}
// de-serialize
std::istream &operator >> (std::istream &is)
{
uint32_t size;
is.read(reinterpret_cast<char*>(&size), sizeof(uint32_t));
m_ID = UReadBinaryString::read(is, size);
return is;
}
};
I haven't coded C++ in a long time, but perhaps you could serialize an array of char.
Then, when you open your file, your string would just point to the array.
Just an idea.