How to write strings as binaries to file? - c++

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

Related

I am trying to loop through a vector of objects and print each element of the objects

I am making an aircraft air control system. There is an aircraft object and there is a vector called "aircraftList_" which holds all the aircraft objects.
I am trying to write a method called "ListAllAircraft" which will print all the contents of aircraftList, could someone help?
Below are my first two guesses at it, neither worked
std::vector<Aircraft> ListAllAircraft(std::vector<Aircraft> aircraftList_)
{
std::cout << aircraftList_;
}
std::vector<Aircraft> ListAllAircraft(std::vector<Aircraft> aircraftList_)
{
for (int i = 0; i < sizeof(aircraftList_); i++)
{
std::cout << aircraftList_.at(i);
}
}
Could i get some help?
Use a range-based for loop is an easier way to iterate the vector.
void ListAllAircraft(std::vector<Aircraft> &aircraftList_)
{
for (auto const &aircraft : aircraftList_)
{
std::cout << aircraft;
}
}
Using sizeof is wrong. The sizeof operator queries size of the object in memory. This would include all the internal members of your specific implementation of std::vector. To query an instance of an std::vector for the number of elements it stores, you use std::vector::size.
For stream output to work though, you need to have a stream insertion operator defined for your Aircraft class. Otherwise the compiler cannot know what you want it to print when you call std::cout << aircraft;.
How this insertion operator should look totally depends on your Aircraft class and what you want to be printed for each aircraft. It could be something like this:
std::ostream& operator<<(std::ostream& stream, const Aircraft& aircraft)
{
stream << aircraft.name
return stream;
}
none of your function are returning a std::vector
It seems they return void. This code can not compile

Save struct with an array of pointers into a file

I'm working on c++ and I need to save this struct into a file:
struct myStruct{
int value;
int value2;
MyClass * myClass[10];
};
The way that I'm saving this struct is the following:
myStruct my_struct;
my_struct.value = 1;
my_struct.value2 = 2;
for ( int i = 0; i < 10; i++){
my_struct.myClass[i] = new MyClass();
}
FILE* f = fopen(path.c_str(), "wb");
if ( f != NULL){
fwrite(&my_struct, sizeof(myStruct), 1, f);
fclose(f);
}
But, when I want to read this file, my program crashes when try to access to the array of "MyClass":
FILE* f = fopen(path.c_str(), "rb");
if ( f != NULL){
fread(&my_struct2, sizeof(struct myStruct), 1, f);
fclose(f);
}
for ( int i = 0; i < 10; i ++ ){
if ( my_struct2.myClass[i] != NULL ){
//Here is the crash
}
}
I've been searching but I can't find a solution. I only find topics about arrays of structs. I know that maybe I'm not searching very well.
Thanks.
Your MyStruct contains twenty pointers to other structures.
By fwrite()ing the contents of your MyStruct to a file, you have successfully written twenty raw memory addresses of your other structures into the file, together with the other members of the MyStruct class.
Which, of course, is utterly meaningless when you try to read them back in another process. You've read back twenty raw memory addresses. Which mean nothing to a completely unrelated process. And, accessing those memory addresses, unsurprisingly, leads to a crash since those memory addresses, for all intents and purposes, are completely random values.
What your code needs to do is not write twenty raw pointer addresses to the file, but the contents of those pointers, and what they point to.
I want to add some things to Sam's answer, even if I know this is not code review, you are writing C in C++.
C++ is not meant to be coded in C, it doesn't want to... It fought its entire life to break its bound with its deprecated father, to surpass him, to explore new meanings and way to solve problems and build efficient code. Don't do this to him... (I love C by the way, deprecated was a joke obviously ;) )
Here's how I'd do it:
#include <fstream>
#include <iostream>
class MyClass
{
public:
MyClass() : _type(-1) {}
MyClass(int type) : _type(type) {}
inline const int &type() const
{ return _type; }
private:
int _type;
};
// -- overload of operator<< that permits me to write a MyClass* to a stream
std::ostream &operator<<(std::ostream &stream, MyClass *myClass)
{
stream << "myClass::type: " << myClass->type();
return stream;
}
struct MyStruct
{
int value;
int value2;
MyClass *myClasses[10];
MyStruct()
{
value = -1;
value2 = 1;
for (std::size_t i = 0 ; i < 10 ; ++i)
{ myClasses[i] = new MyClass(-i); }
}
};
// -- overload of operator<< that permits me to write a MyStruct to a stream
std::ostream &operator<<(std::ostream &stream, const MyStruct &myStruct)
{
stream << "myStruct::"
<< "\n\t value: " << myStruct.value
<< "\n\t value2: " << myStruct.value2
<< "\n\t myClasses: ";
for (std::size_t i = 0 ; i < 10 ; ++i)
{ stream << "\n\t\t " << myStruct.myClasses[i]; }
return stream;
}
int main()
{
std::ofstream outputFile("output.txt");
if (outputFile.is_open() == false)
{ std::cerr << "Could not open file." << std::endl; return -1; }
outputFile << MyStruct() << std::endl; // -- this is where the information is written into the file
outputFile.close();
}
See simple way to write a struct, you could even get it back into the struct the same way with operator>> overload, bonus is you can use on any ostream, which means it will work with sstream, std::cout and everything!
Still this is not really c++-like as there is too much (unprotected) pointers and unchecked magical number sizes (MyClass *myClasses[10]; this is a no-no for me, because it implies this thing: for (std::size_t i = 0 ; i < 10 ; ++i), and this shit gives me shivers).
I would probably use an std::array here
, but I wanted to keep MyStruct as you defined it so the example stay "close" to what you wrote. Another way would have been to use std::unique_ptr or std::shared_ptr.
This can seem as quite a bit of work or intimidating, but you may find that useful in the future. Same goes for using the std containers(array, set, vector, map, etc...), unique_ptr and shared_ptr. But I assure you it's worth giving some time to understand them and learn how to use them. It makes things simpler and safer.
What gave me shivers earlier would be written like this:
std::array<MyClass, 10> myClasses;
Loops would go like this:
for (std::size_t i = 0 ; i < myClasses.size() ; ++i)
{ myClasses[i].type(); }
for (std::array<MyClass, 10>::iterator itC = myClasses.begin() ; itC != myClasses.end() ; ++itC)
{ itC->type(); }
Or even better, a c++11 way to write a loop, that I find easier to read and write:
for (auto myClass : myClasses)
{ myClass.type(); }
Note that if you want to modify myClass inside this one you need to write auto& myClass : myClasses
Hope it helps you.
Using fwrite(&my_struct, sizeof(myStruct), 1, f); is good if your struct my_struct contains purely static data(i.e the data for which memory was allocated at compile time). If it contains dynamic data(i.e the data for which memory is allocated at runtime) then you need to manually store such dynamic data.
Overloading operator<< as shown my #vianney is a good method of saving/serializing dynamic data.

Can an istream variable be a class variable

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

Passing object values to a buffer

I have a class sample as:
class sample{
public:
sample(){
sample_name=new char[10];
}
int sample_num;
float sample_wt;
char *sample_name;
};
Its object is created and values are accesed as folllows:
sample *object= new sample();
object->sample_num=10;
object->sample_wt=20.02;
object->sample_name="test";
My question is as follows:
How will i create a buffer which contains all the information stored inside object?
I tried doing this as follows:
char * buffer = new char[256];
buffer = reinterpret_cast <char *> ( object );
Now,what i see is the object do consists of all the three values of sample_num, sample_wt and sample_name but these values are not passed to buffer,buffer shows garbage values.
So, how will i get these values inside the buffer?
Instead of using <char *> use <void *> if you are using buffers or you can use stl::vector to buffer some object and put these objects to the vector as shown in below code.
'int main()
{
int x;
int size;
vector<BoxOfProduce>box;
cout<<"How many boxes you want";
cin>>size;
for ( x = 0; x < size; x++)
{
BoxOfProduce obj; //create an object
box.push_back(obj); //insert it into the vector
}
for ( x = 0; x < size; x++)
{
box[x].setItemAry();
box[x].randomPick();
box[x].display();
box[x].change();
box[x].display2();
}
getchar();getchar();
return 0;
}'
and you can use Ques, Ques are the best options for buffering.
First Create a structure and store all the values in the structure and put that structure in to the queue.
This code is wrong on so many levels.
Your constructor isn't initializing your variables, but creates an array on the free store, and never deletes it. For default constructor just initialize your members to a default value (likely 0 here and nullptr for sample_name).
Replace
sample *object= new sample();
object->sample_num=10;
object->sample_wt=20.02;
object->sample_name="test";
With a proper constructor, that creates an object with the values given as parameters. Also, to create an object, you don't have to use new by default, you can just write
sample object
To get to your question. If you are using reinterpret_cast to see your object as a char[] you are relying on the fact that the layout of your object may or may not be as you see it in it's definition. This may even work, but you need to make sure that your class fulfills the requirements. Here are some further reading on the topic:
What are POD types in C++?
What are Aggregates and PODs and how/why are they special?
Until that, if you want to serialize your object i.e. read and write it to a stream/buffer, you should write serialize/deserialize functions, reading and writing each member to the stream.
Having in mind #Kerrek SB memory leaks and using standard strings and input/output operators:
#include <ostream>
#include <iostream>
#include <sstream>
class sample
{
public:
sample() {}
int sample_num;
float sample_wt;
std::string sample_name;
friend std::ostream & operator<<(std::ostream &, const sample&);
};
std::ostream & operator<<(std::ostream & os, const sample & sam)
{
os << sam.sample_num << " " << sam.sample_wt << " " << sam.sample_name.c_str();
return os;
}
int main(int argc, const char * argv[])
{
sample *object= new sample;
object->sample_num=10;
object->sample_wt=20.02f;
object->sample_name="test";
std::stringstream buffer;
buffer << *object;
std::cout << buffer.str();
delete object;
return 0;
}

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.