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.
Related
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.
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.
While developing a program in C++ using VS2010 , can I define
std::istream streamRead(ReadBuf&); // struct ReadBuf : public std::streambuf declared before
and use this streamRead in multiple functions in my program?
If not, can anyone suggest me how to read a stream using getline. I have to read the same stream from different functions.
Thank you in advance.
EDIT:
The struct declared in my header file is as below:
struct ReadBuf : public std::streambuf
{
ReadBuf(PBYTE s,size_t n)
{
setg((char*)s,(char*) s,( char*)s + n);
}
};
I have a buffer in memory and the input to my program is its pointer and size. Using the above structure, I copy it to a streambuffer. Now I have to read this streambuffer line by line. This is my requirement.
For example some of my functions are:
int GetSessionN(int session_id,SessionDetail &N_session);
int GetInstanceId(string header,SessionDetail &N_session);
int GetDriverDetails(string body_data,SessionDetail &N_session);
I have to read the first n lines from the stream using GetSessionN and then the successive n lines in the next function and so on.
This is where I initialise the object of ReadBuf. I am not able to initialize it globally.
int SetupLogReader::ProcessLogFile(PBYTE &mem_ptr, ULONG &size)
{
string read;
ReadBuf buf(mem_ptr, size);
istream streamRead(&buf);// Not able use StreamRead declared in header here.
}
you should not copy the stream when returning it in the function but reference it, i.e:
std::istream &streamRead(ReadBuf&){
if (_stream == null){
// create stream
_stream = [newly created stream];
}
return _stream;
}
Edit:
You could also use std::istringstream as it already provides the functionality you are looking for:
from istringstream manual:
std::string stringvalues = "line1\nline2";
std::istringstream iss (stringvalues);
for (int n=0; n<2; n++)
{
char val[256];
iss.getline(val, 256);
std::cout << val << '\n';
}
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;
}
This is a C++ question. I have a class that contains a string:
class MyClass{
public:
std::string s;
};
And I have an array of MyClass objects:
MyClass * array = MyClass[3];
Now I want to write the array as binaries into a file. I cannot use:
Ofstream.write((char *)array, 3 * sizeof(MyClass))
because the size of MyClass varies.
How can I use Ofstream.write to achieve the purpose? Many thanks.
Overload operator<< for your class. You could do it as follows:
ostream& operator<< (ostream& os, const MyClass& mc)
{
return os << mc.s /* << ... other members*/ << endl;
}
In C++ it is usually done using the BOOST serialization class
Programmatically you could do something like this:
Writing:
std::ofstream ostream("myclass.bin",std::ios::binary);
if (!ostream) return; // error!
std::size_t array_size = 3;
ostream.write(reinterpret_cast<char*>(&array_size),sizeof(std::size_t));
for(MyClass* it = array; it != array + array_size; ++it)
{
MyClass& mc = *it;
std::size_t s = mc.s.size();
ostream.write(reinterpret_cast<char*>(&s),sizeof(std::size_t));
ostream.write(mc.s.c_str(),s.size());
}
Reading
std::ifstream istream("myclass.bin",std::ios::binary);
if (!istream) return; // error!
std::size_t array_size = 0;
istream.read(reinterpret_cast<char*>(&array_size),sizeof(std::size_t));
array = new MyClass[array_size];
for(MyClass* it = array; it != array + array_size; ++it)
{
MyClass& mc = *it;
std::size_t s;
istream.read(reinterpret_cast<char*>(&s),sizeof(std::size_t));
mc.resize(s);
istream.read(mc.s.c_str(),s.size());
}
istream.close(); // not needed as should close magically due to scope
Write an insertion operator for MyClass, like this, that writes out its members to the stream one by one. Then make a loop that walks your array, writing each member to the stream. Remember to write out the array size at some point too, so you know how many members to read when you read the file back.
And, as Klaim says, make sure you open the stream in binary mode.
What exactly do you want to write to file? In C++, you can't make assumptions about the content of an object like you can do in C. std::string for instance typically holds pointers, allocators, string lengths and/or the first few characters. It will certainly not hold the entire char[] you'd get from string::data(). If you have a std::string[3], the three sring::data() arrays will (almost certainly) be non-contiguous, so you will need three writes - each call can only write one contiguous array.
A good way of doing this would be to override the << operator for MyClass:
ostream& operator << (ostream& output, const MyClass& myClass)
{
return output << myClass.Value;
}
You can then simply serialise the strings out of MyClass directly into the file stream:
std::fstream fileStream("output", std::ios::out|std::ios::binary);
for (int i = 0; i < 3; ++i)
fstream << array[i];
Open the stream in binary mode:
std::fstream filestream( "file.name", std::ios::out | std::ios::binary );