C++ Read/Write struct object to a file - c++

So i written a program where i can input 4 values a first name, last name, height and a signature. I store all values in a Vector but now i would like to learn how i can take the values from my vector and store them in a file and later on read from the file and store back into the vector.
vector<Data> dataVector;
struct Data info;
info.fname = "Testname";
info.lname = "Johnson";
info.signature = "test123";
info.height = 1.80;
dataVector.push_back(info);
Code looks like this i havent found anyway to store objects of a struct into a file so i'm asking the community for some help.

You should provide your struct with a method to write it to a stream:
struct Data
{
// various things
void write_to(ostream& output)
{
output << fname << "\n";
output << lname << "\n";
// and others
}
void read_from(istream& input)
{
input >> info.fname;
input >> info.lname;
// and others
}
};
Or provide two freestanding functions to do the job, like this:
ostream& write(ostream& output, const Data& data)
{
//like above
}
// and also read
Or, better, overload the << and >> operator:
ostream& operator<<(const Data& data)
{
//like above
}
// you also have to overload >>
Or, even better, use an existing library, like Boost, that provides such functionality.
The last option has many pros: you don't have to think how to separate the fields of the struct in the file, how to save more instances in the same file, you have to do less work when refactoring or modifying the struct.

Don't reinvent the wheel: use the Boost serialization libraries.

Related

Problems while opening a .dat file in c++

so basically I was trying to save a class inside a .dat file but in my code but it says this error No matching member function for call to 'open' but I put fstream header. I don't know if I'm writing something wrong. I use Xcode 10.
class memberinformation
{
string name; //name
long int phonenumber; // phone number
int memberid; // member id
public :
memberinformation()
{ name="not assigned" ;
phonenumber=0;
memberid=0;
}
int option3();
int option2();
int option1();
int option4();
};
void wrt_file() //file function
{
memberinformation k;
fstream f;
f.open ("information.dat",ios::app,ios::binary) //this is where I get the error.
f.write((char*)&k,sizeof(k));
}
You are lucky to have been stopped by a simple error. #Alex44 has already shown how to get rid of the error:
f.open ("information.dat",ios::app|ios::binary); //this is where I get the error.
But the following line is even worse:
f.write((char*)&k,sizeof(k));
because the compiler will not show any error, while the content of the string will not be saved in the file. std::string is not trivially copiable and because of that, the memberinformation class is not either. So you should not try to write it to a file as raw bytes.
You should instead write a serialization function that writes to a binary stream (just a possible serialization way):
phonenumber as a long int (no problem there)
memberid as an int (no problem there)
name.size as a size_t
name.data as name.size bytes
The other two answers have answered:
Why its not compiling.
Why its a bad idea to write binary objects.
I would suggest that you serialize the object via the standard C++ technique of using the stream operators. This makes writting/reading the objects trivial and usually makes debugging problems easy.
Using the format suggested by #serge-ballesta in his post:
class memberinformation
{
string name; //name
long int phonenumber; // phone number
int memberid; // member id
public :
// OLD STUFF GOES HERE
void swap(memberinformation& other) noexcept
{
using std::swap;
swap(name, other.name);
swap(phonenumber, other.phonenumber);
swap(memberid, other.memberid);
}
friend std::ostream& operator<<(std::ostream& str, memberinformation const& data)
{
return str << data.phonenumber << " "
<< data.memberid << " "
<< data.name.size() << " "
<< data.name << " ";
}
friend std::istream& operator<<(std::istream& str, memberinformation& data)
{
memberinformation tmp;
std::size_t nameSize
if (str >> tmp.phonenumber >> tmp.memberid >> nameSize) {
// All sizes were read correctly.
tmp.name.resize(nameSize);
if (str.ignore(1).read(&tmp.name[0], nameSize)) {
// ignored the space and read the name correctly.
// So now we update the "data" object
tmp.swap(data);
}
}
return str;
}
};
Now in your code:
int main()
{
memberinformation object;
std::cout << object;
std::cin >> object;
std::ofstream file("Data.dat");
file << object;
}
You miss a semicolon and you need to "bitwise or" your flags:
void wrt_file() //file function
{
memberinformation k;
fstream f;
f.open ("information.dat",ios::app|ios::binary); //this is where I get the error.
...
}
The answers above address your initial problem. I'm going to talk about two more.
First, you probably should f.close() at the end of your method. It may be perfectly fine to let it drop out of scope and clean up from there, but I personally think that's ugly, and I wouldn't count on it.
Second, I wouldn't store the data in binary unless there's a really good reason to do it. It won't be portable. Serge above suggests a serialization method. I'd consider an alternate approach. I'd write to the file in a human readable form such as JSON. Yes, it's a little more work, but...
-If you change your class, your old files will still be readable
-They are portable across environments
-You can actually look at them and readily understand what they contain
So Serge's suggestions above aren't horrible, but I'd pick a more modern serialization / deserialization style.
Note that your f.write won't work because your object contains other objects, you don't know how they work under the hood. That string, for instance, almost certainly can't be dumped the way you're trying to do it. Plus you aren't only dumping your data.
Also, you should printf the sizeof(k). You might find it interesting information. Try to account for every byte. You could printf the sizeof(k.name) to help you work some of it out.
I'm almost positive the information doing so would surprise you, but I haven't actually done it myself, because I would never try to raw memory copy C++ objects, and that's in effect what you're trying to do.

C++ Best approach to filtering bytes in a stream

I'm learning c++ (coming from a C and Java university coursework) and today I want to write a class that filters the bytes taken from a generic stream and writes its output to another stream.
To be coincise, let's say I want to make a class that base64-encodes the input and writes the output to stdout.
In bash I would write:
echo "some input data" | base64
In C++ i want to implement a class MyB64Encoder that would behave like this:
std::cout << myB64EncoderObject << "some input data";
//Alternatively, is it possible to make it like this?
std::cout << MyB64Encoder << "some input data";
The thing is, the myB64EncoderObject has, of course, to maintain an internal state and an internal buffer. To prevent blocking and excessive memory usage, it must be able to read and process small chunks of data and output each one of them immediately after it has been processed.
There are a few more things to take care of:
The object must wait for the output stream to be able to receive data
The object must throw an error if there is no stream reading from it (kinda like a broken pipe?)
What would be the best approach to a problem like this, in terms of efficiency? How would I implement it in modern C++1x?
The existing things that behave like this:
std::cout << myB64EncoderObject << "some input data";
are I/O manipulators (eg. std::boolalpha, std::hex, ...). However, these just set flags on the stream that it already knows how to interpret.
If you want to keep that syntax, you'll need to something more complex, namely an intermediate wrapper:
class B64Wrapper {
std::ostream &os_;
B64Encoder &enc_; // only if your encoder is really stateful
public:
B64Wrapper() = delete;
B64Wrapper(B64Wrapper&&) = default;
B64Wrapper(B64Wrapper const&) = default;
B64Wrapper(std::ostream &os, B64Encoder &enc) : os_(os), enc_(enc) {}
template <typename T>
B64Wrapper& operator<< (B64Wrapper &self, T val) {
self.enc_.encode(os_, val);
return self;
}
};
B64Wrapper operator<< (std::ostream &os, B64Encoder &enc) {
return B64Wrapper(os, enc);
}
(note you still need to write the B64Encoder::encode(std::ostream &, T value) method).
If your encoder isn't really stateful, you don't need a reference to it, and declare B64Encoder as an empty tag type with a global instance to get the same effect - in that case it only exists to select the operator<< overload.
The other approach is to write a std::basic_streambuf implementation which encodes the input to sputc/sputn/xsputn. It can forward everything else to a wrapped streambuf or to the base class, depending on what you inherit from.
You can do something like this:
class MyEncoder
{
public:
private:
std::ostream* os = nullptr;
// This overload deals with:
// std::cout << myEncoder ...
friend MyEncoder& operator<<(std::ostream& os, MyEncoder& me)
{
// grab a reference to the target output stream
me.os = &os;
return me;
}
// This overload deals with:
// std::cout << MyEncoder() ...
friend MyEncoder& operator<<(std::ostream& os, MyEncoder&& me)
{
// the temporary is currently bound to the l-value parameter me
// so we can just pass this call on to the previous overload
return os << me;
}
// This overload deals with:
// myEncoder << <anything else>
template<typename T>
friend MyEncoder& operator<<(MyEncoder& me, T const& v)
{
// only encode if there is an output stream to send the data to
// this will only be set if one of the above overloads was called
if(!me.os)
throw std::runtime_error("no stream to receive encoded data");
// do your encoding here
(*me.os) << "{encoded: " << v << "}";
return me;
}
};
Basically to achieve this:
std::cout << MyEncoder() << "some data: " << 45;
// ^ calls operator<<(MyEncoder&, 45)
// ^ calls operator<<(MyEncoder&, "some data: ")
// ^ calls operator<<(std::cout, MyEncoder())
The calls go left to right.
It may seem a little involved but it is basically covering 3 different call possibilities.
MyEncoder encoder;
std::cout << encoder; // MyEncoder& object
std::cout << MyEncoder(); // (temporary) MyEncoder&& object
encoder << "anything else" // A MyEncoder& receiving any other object
The first 2 operators are overloaded to set the internal std::ostream* and the third operator is overloaded to do the actual encoding.

Ways to write a vector of a class to a file

I'm not sure how I should word this, so I'll attempt to put it in code. (This has many errors I know it will not compile it is simply to show what I want to do because I can't put it in words)
using namespace std; //for correctness sake
class foo {
public:
foo(int a=0, int n=0);
void stuff(int a);
void function(int n);
const int get_function() {return n;}
const int get_stuff(){return a;}
private:
int n, a;
};
struct Store{
public: get_foo(){return vector<f.foo>;} //I'm not too sure of the syntax here but you get the idea
private:
foo f;
}
Basically I want to take all the information that is returned in class foo, and output this, formatted, to a file. Thing is, I need to make many of these within the file and it has to be able to read it back for it to be worth anything.
So just appending each consecutive foo class to the file won't work(at least I don't see how).
I tried using ostream to overload the << operator, but I'm not sure how to call it to write it to the file. Any suggestions are welcome! Thanks.
I think your Store should be like this:
struct Store
{
public:
std::vector<foo> get_foo()
{
return f;
}
private:
std::vector<foo> f;
};
To overload << of std::ostream:
std::ostream& operator<<(std::ostream& out, const Store& f)
{
for (auto &x : f.get_foo())
out << x.get_function() << ", " << x.get_stuff() << "\n";
return out;
}
//Without auto
std::ostream& operator<<(std::ostream& out, const Store& f)
{
std::vector<foo> q = f;
for (int i=0; i<q.size(); i++)
out << q[i].get_function() << ", " << q[i].get_stuff() << "\n";
return out;
}
There are many tings wrong with your code.
So many that it's clear that you never read any C++ book and are just experimenting with a compiler.
Don't do that. C++ is really the worst language in the world to approach that way for many independent reasons.
No matter how smart you are.
Actually being smart is sort of a problem in certain areas because many C++ rules are not the result of a coherent logical design, but of historical evolution and committee decisions. Not even Hari Seldon would be able to foresee correctly what a committee would decide, you cannot deduce history.
Just pick a good book and read it cover to cover. There is no other sensible way to learn C++.
About writing structs to a file the topic is normally called "serialization" and takes care of the slightly more general problem of converting live objects into a dead sequence of bytes (written to a file or sent over the network) and the inverse problem "deserialization" of converting the sequence of bytes back into live objects (on the same system, on another identical system or even on a different system).
There are many facets of this problem, for example if your concern is about portability between systems, speed, size of the byte sequence, ability to reload bytes sequences that were saved back when your classes were slightly different because you evolved the program (versioning).
The simplest thing you can do is just fwrite things to a file, but this is most often simply nonsense in C++ and is a terrible way for many reasons even when it's technically possible. For example you cannot directly fwrite an std::vector object and hope to read it back.
I think you need something like this:
template<typename T>
std::ostream& operator << (std::ostream& out, const std::vector<T*>& elements)
{
for (size_t i = 0; i < elements.size(); i++)
out << elements[i] << ", ";
return out << std::endl;
}

Reading/writing files to/from a struct/class

I'd like to read a file into a struct or class, but after some reading i've gathered that its not a good idea to do something like:
int MyClass::loadFile( const char *filePath ) {
ifstream file ( filePath, ios::in | ios::binary );
file.read ((char*)this, 18);
file.close();
return 0;
}
I'm guessing if i want to write a file from a struct/class this isn't kosher either:
void MyClass::writeFile( string fileName ) {
ofstream file( fileName, ofstream::binary );
file.write((char*)this, 18);
file.close();
}
It sounds like the reason i don't want to do this is because even if the data members of my struct add up to 18 bytes, some of them may be padded with extra bytes in memory. Is there a more correct/elegant way to get a file into a class/struct like this?
The preferred general technique is called serialization.
It is less brittle than a binary representation. But it has the overhead of needing to be interpreted. The standard types work well with serialization and you are encouraged to make your class serialize so that a class containing your class can easily be serialized.
class MyClass {
int x;
float y;
double z;
friend std::ostream& operator<<(std::ostream& s, MyClass const& data);
friend std::istream& operator>>(std::istream& s, MyClass& data);
};
std::ostream& operator<<(std::ostream& s, MyClass const& data)
{
// Something like this
// Be careful with strings (the input>> and output << are not symmetric unlike other types)
return str << data.x << " " << data.y << " " << data.z << " ";
}
// The read should be able to read the version printed using <<
std::istream& operator>>(std::istream& s, MyClass& data)
{
// Something like this
// Be careful with strings.
return str >> data.x >> data.y >> data.z;
}
Usage:
int main()
{
MyClass plop;
std::cout << plop; // write to a file
std::cin >> plop; // read from a file.
std::vector<MyClass> data;
// Read a file with multiple objects into a vector.
std::ifstream loadFrom("plop");
std::copy(std::istream_iterator<MyClass>(loadFrom), std::istream_iterator<MyClass>(),
std::back_inserter(data)
);
// Write a vector of objects to a file.
std::ofstream saveTo("Plip");
std::copy(data.begin(), data.end(), std::ostream_iterator<MyClass>(saveTo));
// Note: The stream iterators (std::istream_iterator) and (std::ostream_iterator)
// are templatized on your type. They use the stream operators (operator>>)
// and (operator<<) to read from the stream.
}
The answer is : there is no silver bullet to this problem.
One way you can eliminate the padding to ensure that the data members in your class is to use(in MSVC which you are using)
#pragma pack( push, 1 )
class YourClass {
// your data members here
int Data1;
char Data2;
// etc...
};
#pragma pack( pop )
The main usefulness of this approach is if your class matches a predefined format such as a bitmap header. If it is a general purpose class to represent a cat, dog, whatever then dont use this approach. Other thing if doing this is to make sure you know the length in bytes of the data types for your compiler, if your code is EVER going to be multi platform then you should use explicit sizes for the members such as __int32 etc.
If this is a general class, then in your save member, each value should be written explicitly. A tip to do this is to create or get from sourceforge or somewhere good code to help do this. Ideally, some code that allows the member to be named, I use something similar to :
SET_WRITE_DOUBLE( L"NameOfThing", DoubleMemberOfClass );
SET_WRITE_INT( L"NameOfThing2", IntMemberOfClass );
// and so on...
I created the code behind these macros, which I am not sharing for now but a clever person can create their own code to save named to stream in an unordered-set. This I have found is the perfect approach because if you add or subtract data members to your class, the save/load is not dependent on the binary representation and order of your save, as your class will doubtless evolve through time if you save sequentially this is a problem you will face.
I hope this helps.

writing a tf::transform object to a file

I'm attempting to do something resembling the following block of code:
tf::transform t;
initializeTransform(t);
std::ofstream f;
f.open("somefile");
f << t << std::endl;
f.close();
Assuming that I've properly set up that f and t when I'm trying to write t to f, how would I do so? I tried a number of variants of this, and all of them result in a huge wall of text to the effect that ofstream doesn't know how to handle a tf::transform object, which isn't too surprising.
Is there some way to make ofstream take arbitrary objects? Is there some format that I could readily convert it to that's more conducive to streaming? Ideally, if I convert it, I'd like to have a way to reversibly convert it to some matrix that I can pipe straight into and out of a file.
Implement the operator
I'm not sure of the contents of the transform struct in this case, but assuming it is:
struct transform { float mat[16]; }
Then the implementation can be something like:
std::ostream& operator<< (std::ostream& os, const tf::transform& t)
{
os << t.mat[0];
for(int i=1;i<16;++i) os << ',' << t.mat[i];
return os;
}