Saving a struct array to an external file in c++ - c++

I have an assignment where I need to:
save the list that the user inputs to an external file.
load the info from the file previously saved.
I managed to write in the code for the 1st task, but since I have errors, I couldn't continue to the 2nd task. Please take a look and let me know what your thoughts are.

First Error:
When you create an array, the name of the array is a pointer to the beginning of where the array is in memory. In line 42, you cannot compare an int with a pointer like that. Instead, I assume you want to do this:
for (int i = 0; i < size; ++i) {
Second Error:
In line 43, you are trying to input an std::ofstream object into a function. In order to do this, std::ofstream must be copy-able. ofstream has a deleted copy constructor, meaning that it cannot be copied and thus cannot be passed as an input to a function. Instead, you could simply create the ofstream object and open the file within your pet::save function. Also, make sure you close the ofstream. As an example:
void pet::save()
{
ofstream file;
out.open("animal.txt");
if (!out.is_open())
cout << "Unable of open file." << endl;
file << pet_info << endl;
file.close();
}
You could also use a pointer to the ofstream as an input to your save function, since pointers can be copied (then use out->operator<<(pet_info) to input to the file). This would make it run faster, but this situation does not seem to prompt such optimization. The function prototype would look like
void pet::save(ofstream* file);
and you would pass &out as the input to the function.
Third Error:
You are trying to use the array called animal within your pet class. Since animal is an array of pets that is created outside of your pet class, the pet class does not have access to it (so animal was not declared in the scope of pet). I am guessing your pet class stores a string which contains a pet's information (which I call pet_info). Given that is true, you can call the above save function that I wrote for all of your pets in the animal array to save them to a file.
Fourth Error:
On line 109 of pet.cpp, it appears you are missing a semicolon. That could be why the bracket error is there, or you are just missing a bracket.

Related

ofstream returning error "No matching function to call std::basic_ofstream<char>::close(const char [14])"

Need help fixing my code, not sure what's wrong. I'm using C++11, trying to write a vector to a file by individually writing each struct. The section of code returning an error is:
string craigSave = "craigSave.txt";
ofstream file(craigSave.c_str());
file.open("craigSave.txt");
for (int i=0; i<finalVector.size(); i++){
file << finalVector[i]<<endl;
}
file.close("craigSave.txt");
cout<<"Thanks for shopping!"<<endl;
done = true;
The error returned is on the "file.close" line and is:
error: no matching function for call to 'std::basic_ofstream::close(const char [14])'
I research on this error seems to point to needing to use char* as an argument instead of a string, but I'm using C++11, so it should accept strings. Also it is strange that there is no error for the file.open line, as all research shows the error being there, not at file.close
Just use file.close();, there's no need to pass the file name again.
See http://www.cplusplus.com/reference/fstream/ofstream/close/.
Also, ofstreams are RAII objects, which means that the file will automatically be closed once the ofstream object goes out of scope (see do I need to close a std::fstream?):
{
ofstream out("name");
// do something with out...
} // out.close is called automatically

Store and Load objects from a file

I'm making this program for class and we are supposed to store objects from a class in a file and then load them. But I always get the last object stored instead of the first. Tried the seekp function but it doesn't work. Also shouldn't the size of an object be 38 bytes, instead of the 48 i'm getting?
void student::load()
{
fstream fin;
fin.open("StudentData.bin",ios::binary|ios::in);
fin.read((char*)this,sizeof(*this));
}
void student::store(int z)
{
fstream fout;
fout.open("StudentData.bin",ios::binary|ios::out);
//fout.seekp(38*z, fout.beg)
cout<<sizeof(*this);
fout.write((char*)this,sizeof(*this));
}
for(i=0;i<count;i++)
{
s[i].store(i);
}
cout<<"Done!";
student pleb;
pleb.load();
pleb.showstudent();
return 0;
}
Your fout.open() rewrites the file each time. If you want to append to file, that is, to have each store() call to write that student after all other students already written, then you can use the ios::app flag.
Or, better, do not open the file in store() at all. Make your store() actuall accept the stream as a parameter; then open the file in the main program (once per all students) and pass the stream to store(). Not only this will solve your problem, but will also make your student class more configurable as it will be easy to write to any file, or in general to any stream the user of student needs.
BTW, also make sure that it is correct to write your students this way. For example, if you have any pointers in your student (including, say, std::string members as they have pointers inside), you will not get what you expect.
As for the object size, it is impossibly to answer not seeing the whole declaration of student class.

C++ create ifstream/ofstream in a class, associated with pre-made text file

I am having a problem associating an ifstream read and ofstream print to a pre-made text file called finances.txt. This is within a class called Data. So far, this is what I've tried:
I declared ifstream read and ofstream print in the class header file. Then, in the cpp file:
Data::Data(string n, string d)
:name(n),
date(d)
read(name)
print(name)
{
cout << "name = " << name << endl;
read.open(name);
print.open(name);
//...
}
I also tried this, without declaring anything in the header:
Data::Data(string n, string d)
:name(n),
date(d)
{
ifstream read(name);
ofstream print(name);
//...
And just different variations of this kind of thing. The syntax is always correct in the sense that I don't get any errors, but whenever it runs, it acts like the file doesn't exist and creates a new one named finances.txt, which in turn erases all of the text that was in the original. I have done this correctly before and just can't remember what I did and what I am doing incorrectly here.
I am a little confused as to what exactly you are trying to do?
Are you trying to append to the file? Because when you call
ofstream print(name) you are writing over the file that you are reading in.
So if you want to append to that same file you have to add.
fstream::app in the declaration of the ofstream

C++ program.exe has stopped working after reading from binary file

I have made a class Flight with informations to be stored in a binary file called data.txt at another method.That saving of records was working fine, but now I'm having problems reading back the records I've saved. It is working to display all the records till the end of the file (eof). But when records are done displaying, there comes a pop up error saying that Program.exe has stopped working.
void Flight::ViewFlight(){
HANDLE hConsole; //Console colors
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
fstream data;
Flight flight;
data.open("data.txt",ios::in | ios::binary);
if (data.fail())
{
SetConsoleTextAttribute(hConsole, 12);
cout<<"\n\nFlight data does not exist yet";
cout<<"\n\nYou are being redirected to the Main Menu in 3 seconds\n\n";
cout<<"3\n\n";
Sleep(1000);
cout<<"2\n\n";
Sleep(1000);
cout<<"1\n\n";
Sleep(1000);
cout<<"0\n\n";
SetConsoleTextAttribute(hConsole, 15);
}
else{
while(data.read((char*) &flight, sizeof(flight)))
{
if(!data.eof())
{
SetConsoleTextAttribute(hConsole, 10);
cout<<"\n\n----------- Record for "<<flight.flightid<<" -----------\n";
SetConsoleTextAttribute(hConsole, 15);
cout<<"\nFlight Number \t\t: "<<flight.flightnumber;
cout<<"\nDeparture Airport\t: "<<flight.departAirport;
cout<<"\nArrival Airport\t\t: "<<flight.arriveAirport;
cout<<"\nDeparture Time\t\t: "<<flight.departTime.hour<<":"<<flight.departTime.minute;
cout<<"\nDeparture Date\t\t: "<<flight.departDate.day<<"/"<<flight.departDate.month<<"/"<<flight.departDate.year;
cout<<"\nPrice \t\t\t: RM "<<flight.price;
cout<<"\nBusiness Class Seats\t: "<<flight.bseat;
cout<<"\nFirst Class Seats\t: "<<flight.fseat;
cout<<"\nEconomy Class Seats\t: "<<flight.totalseat;
cout<<endl;
}
}
}
data.close();
}
Your Flight class contains std::string members. These are not plain old data types and typically hold pointers to dynamically allocated memory. You can't read and write your class as a unit and hope the std::string members and their contents will be properly constructed. The same may apply to the Time and Data members but you haven't shown how they're defined.
You need to look into proper serialization.
Several related questions:
Serializing a class which contains a std::string
How to write strings as binaries to file?
Question about read and write std::string to Binary Files
The loop seems fine, your file might have corrupted data, probably a not terminated string, or may be there is/are some garbage characters at the end of the input file. to verify comment all the cout statements in the loop, and see if the program stops hanging.
also data.eof() check is redundant, nevertheless it should not hang the program.
just had a look at your flight class, you can not read directly into a class having other class objects. in your case, the string objects. you need to deserliaze the stream and initilize the variables yourself
The problem is when the Flight is getting destructed it is destroying those string objects which are not correctly constructed string objects.
Basically first separate your character strings from your buffer and assign those to your string variables one by one yourself.
Flight structure consist of other classes that point to dynamically allocated memory (heap), for example string flightnumber; is STL string class, that has char* or wchar* inside.
If you save Flight object as a binary buffer, it will save pointer only. While loading the object, you'll get memory address in the pointer, that is invalid. That's the reason you are getting access violation exception, that means you tried to access not allocated memory.
btw that's the best case you got 0xC0000005, in worst case you'd just access memory allocated for other objects, and got trash in your output.
You have to overload operator<< and operator>> for Flight class, and every class member which is not standard library. After you do it you'll just write:
fstream data;
Flight flight;
data >> flight;

Write and read object of class into and from binary file

I try to write and read object of class into and from binary file in C++. I want to not write the data member individually but write the whole object at one time. For a simple example:
class MyClass {
public:
int i;
MyClass(int n) : i(n) {}
MyClass() {}
void read(ifstream *in) { in->read((char *) this, sizeof(MyClass)); }
void write(ofstream *out){ out->write((char *) this, sizeof(MyClass));}
};
int main(int argc, char * argv[]) {
ofstream out("/tmp/output");
ifstream in("/tmp/output");
MyClass mm(3);
cout<< mm.i << endl;
mm.write(&out);
MyClass mm2(2);
cout<< mm2.i << endl;
mm2.read(&in);
cout<< mm2.i << endl;
return 0;
}
However the running output show that the value of mm.i supposedly written to the binary file is not read and assigned to mm2.i correctly
$ ./main
3
2
2
So what's wrong with it?
What shall I be aware of when generally writing or reading an object of a class into or from a binary file?
The data is being buffered so it hasn't actually reached the file when you go to read it. Since you using two different objects to reference the in/out file, the OS has not clue how they are related.
You need to either flush the file:
mm.write(&out);
out.flush()
or close the file (which does an implicit flush):
mm.write(&out);
out.close()
You can also close the file by having the object go out of scope:
int main()
{
myc mm(3);
{
ofstream out("/tmp/output");
mm.write(&out);
}
...
}
Dumping raw data is a terrible idea, from multiple angles. This will break even worse once you add pointer data.
One suggestion would be to use Boost.Serialization which allows for far more robust data dumping.
Your main problem is the file does not contain the contents yet due to fstream buffering. Close or flush the file.
I'll echo "you shouldn't be doing this". If you print out sizeof(myc) in the code above it's probably 4, as you'd expect... BUT try changing read and write to be virtual. When I did so, it prints out the size as 16. Those 12 bytes are internal guts with sensitive values—and saving them out and then reading them back in would be like expecting a pointer value to be still good if you wrote it and loaded it again.
If you want to circumvent serialization and map C++ object memory directly to disk, there are ways to hack that. But rules are involved and it's not for the faint of heart. See POST++ (Persistent Object Storage for C++) as an example.
I'll add that you did not check the fail() or eof() status. If you had you'd have known you were misusing the fstream API. Try it again with:
void read(ifstream *in) {
in->read((char *) this, sizeof(myc));
if (in->fail())
cout << "read failed" << endl;
}
void write(ofstream *out){
out->write((char *) this, sizeof(myc));
if (out->fail())
cout << "write failed" << endl;
}
...and see what happens.
My C++ is pretty rust and highly under-tested, but you may want to take a look at Serialization and Unserialization. FAQ
I've done something similar using output.write((char*)&obj, sizeof(obj)), obj being an instance of your class. You may want to loop this if you want to write the data inside the object instead, which would generally be the case as you need members to be readable, right ?
Same thing for reading with read function. But if you have dynamic allocation to do then with this data, you need to handle it.