I've got a custom class that is as follows:

class course {
   private:
      string prefix;
      int number;
      string title;
      int credits;
      char grade;
   public:
      //Constructors
      course();
      course(string, int, string, int, char);
      //Accessors
      void set();
      void print() const;
      string get_prefix() const;
      int get_number() const;
      string get_title() const;
      int get_credits() const;
      char get_grade() const; 
      //Operators
      bool operator < (course) const;
      bool operator > (course) const;
      course operator = (course);
      bool operator == (course) const;
      bool operator == (int n) const;
      bool operator != (course) const;
};

//.... then in main

vector<course> cache; //holds courses in memory
vector<course>::iterator point; //points at the cache
course temp; //buffer
fstream file;//for file I/O

And I'm trying to do file input and output as follows:

cout << "Loading file..." << endl;
file.open("Courses.dat", ios::in | ios::binary);
file.clear();
file.read(reinterpret_cast<char *>(&temp), sizeof(temp));
while (!file.eof()) { 
   cache.push_back(temp);
   temp.print();
   file.read(reinterpret_cast<char *>(&temp), sizeof(temp));
}
file.close();
cout << "Saving to file..." << endl;
file.open("Courses.dat", ios::out | ios::trunc | ios::binary);
file.clear();
for(point = cache.begin(); point != cache.end(); point++) {
   temp = *point;
   temp.print();
   file.write(reinterpret_cast<char *>(&temp), sizeof(temp));
   file.clear();
}     
file.close();

What's interesting is if I write and read pre-made data like below, it works:

course one("CSCI", 2012, "C++ Programming 2", 3, 'A');
course two("MATH", 2081, "Multiple Varible Calculus", 4, 'C');
course three("ENGL", 1022, "Composition 2", 3, 'B');
file.open("Courses.dat", ios::out | ios::trunc | ios::binary);
file.write(reinterpret_cast<char *>(&one), sizeof(one));
file.write(reinterpret_cast<char *>(&two), sizeof(two));
file.write(reinterpret_cast<char *>(&three), sizeof(three));
file.close();

//close program, open it again without writing that data
//load data into cache, and it displays correctly

.But if I use any custom data (AKA user-input) it saves correctly as far as I can tell, but if I try to load the data it crashes when it tries to display it - printing out random gibberish.


Can anyone spot any errors I missed in the file I/O routines? If need be I can post the entire source code.
Thanks in advance for wading through this post!

Recommended Answers

All 10 Replies

Your class doesn't meet the requirements for a POD type, so using read and write is undefined. Add a couple of member functions that serialize your class to and from a string, then use that string for file I/O.

Um... by that do you mean overload the << and >> operators?

I'm basing my work off my textbook ("Object-Oriented Programming in C++" by Robert Lafore"), who uses the following example:

// diskfun.cpp
// reads and writes several objects to disk
#include <fstream>                //for file streams
#include <iostream>
using namespace std;
////////////////////////////////////////////////////////////////
class person                      //class of persons
   {
   protected:
      char name[80];              //person's name
      int age;                    //person's age
   public:
      void getData()              //get person's data
         {
         cout << "\n   Enter name: "; cin >> name;
         cout << "   Enter age: "; cin >> age;
         }
      void showData()             //display person's data
         {
         cout << "\n   Name: " << name;
         cout << "\n   Age: " << age;
         }
   };
////////////////////////////////////////////////////////////////
int main()
   {
   char ch;
   person pers;                   //create person object
   fstream file;                  //create input/output file
                                  //open for append
   file.open("GROUP.DAT", ios::app | ios::out |
                                      ios::in | ios::binary );
   do                             //data from user to file
      {
      cout << "\nEnter person's data:";
      pers.getData();             //get one person's data
                                  //write to file
      file.write( reinterpret_cast<char*>(&pers), sizeof(pers) );
      cout << "Enter another person (y/n)? ";
      cin >> ch;
      }
   while(ch=='y');                //quit on 'n'
   file.seekg(0);                 //reset to start of file
                                  //read first person
   file.read( reinterpret_cast<char*>(&pers), sizeof(pers) );
   while( !file.eof() )           //quit on EOF
      {
      cout << "\nPerson:";        //display person
      pers.showData();            //read another person
      file.read( reinterpret_cast<char*>(&pers), sizeof(pers) );  
      }
   cout << endl;
   return 0;
   }

Do you mean because the strings are of undefined length, the file I/O messed up?

NOTE: Interestingly, when I tried again, here is the output:

CSCI 2012: C++ Programming 2
   Worth 3 credits. Grade: A
MATH 2081: Multiple Varible Calculus
   Worth 4 credits. Grade: C
ENGL 1022: Composition 2
   Worth 3 credits. Grade: B
 2022:
   Worth 4 credits. Grade: C

Notice the last entry: it read the integers and char correctly, just missed the strings. Not crashing anymore...

>Um... by that do you mean overload the << and >> operators?
If you want. I think explicit serialize and deserialize member functions are more flexible though.

>I'm basing my work off my textbook ("Object-Oriented Programming in C++" by Robert Lafore")
You have my sympathy then. Lafore doesn't come to mind when I think of good writers, and he's on the list when I think of bad writers.

>Do you mean because the strings are of undefined length, the file I/O messed up?
I mean that your objects aren't guaranteed to be convertable to an array of unsigned char due to padding and other internal data structure. That's an absolute requirement when using read or write because you're casting your object to an array of char (roughly the same thing). Do a google search for "plain old data". Those are the rules you have to abide by if you want to be able to cast your objects to an unsigned char* (and you should be using unsigned char* rather than char*).

>Not crashing anymore...
Undefined behavior means anything could happen. It could work for a year and then crash every day for a month straight or vice versa.

Gaaaah, okay, now its crashing again...

Yea, I'm also using a textbook by Tony Gaddis, it's just not with me.

Hmm... so if I created a struct to hold my data, built a class around that struct, then wrote only the STRUCT to disk it would work better? I'll start on that, yell at me if I missed the boat again.


Sorry for the confuzzlement, I've never heard of any of this POD stuff, our professor just throws up powerpoints and hopes we get it ><

Wow. I have to re-write everything to deal with structs?
This sucks.

Ooooookay. So I re-wrote everything to write a struct to file, instead of a class. I made a struct called cinfo that I create from a class to write a file, and made a constructor to create a class from one of those structs (for reading data back in).

But the same exact error happens! I must be missing something different.

// This uses classes and STL to manage a listing of courses.
// Created by:
// Created: 5/3/07
// Modified: 5/4/07
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <functional> // (?)
using namespace std;

struct cinfo { //holds course info
   string prefix;
   int number;
   string title;
   int credits;
   char grade;
};

class course {
   private:
       string prefix;
       int number;
       string title;
       int credits;
       char grade;
   public:
      //Constructors
      course();
      course(string, int, string, int, char);
      course(cinfo in);
      //Accessors
      void set();
      void print() const;
      string get_prefix() const;
      int get_number() const;
      string get_title() const;
      int get_credits() const;
      char get_grade() const;
      cinfo get_info() const;
      //Operators
      bool operator < (course) const;
      bool operator > (course) const;
      course operator = (course);
      bool operator == (course) const;
      bool operator == (int n) const;
      bool operator != (course) const; 
}; 

//class functions...
//indenting below is a little screwed up, my bad

int main()
{
   vector<course> cache; //holds courses in memory
   vector<course>::iterator point; //points at the cache
   cinfo temp        //buffer struct
   course *buffer; //buffer class
   fstream file;//for file I/O
 
  //stuff happens

 file.open("Courses.dat", ios::out | ios::trunc | ios::binary);
   temp = one.get_info();
   file.write(reinterpret_cast<char *>(&temp), sizeof(temp));
   temp = two.get_info();
   file.write(reinterpret_cast<char *>(&temp), sizeof(temp));
   temp = three.get_info();
   file.write(reinterpret_cast<char *>(&temp), sizeof(temp));
   file.close();



cout << "Saving to file..." << endl;
file.open("Courses.dat", ios::out | ios::trunc | ios::binary);
file.clear();
for(point = cache.begin(); point != cache.end(); point++) {
   temp = point->get_info();
   file.write(reinterpret_cast<char *>(&temp), sizeof(temp));
   file.clear();
}     
file.close();//close file



 cout << "Loading file..." << endl;
 file.open("Courses.dat", ios::in | ios::binary);
 file.clear();
 file.read(reinterpret_cast<char *>(&temp), sizeof(temp));
 while (!file.eof()) {  //read in file
     buffer = new course(temp);
     cache.push_back(*buffer);//place object in cache
     delete buffer;
     file.read(reinterpret_cast<char *>(&temp), sizeof(temp));
}
file.close();

Attached is the entire source code. I cannot upload the blank "Courses.dat" file, so create a new ".txt" file in the same folder and rename it "Courses.dat" for it to work. The prompt at the beginning allows you to add three structs to file, so you don't have to type data every time you blank the .dat file.

>Wow. I have to re-write everything to deal with structs?
No. You're so focused on trying to write an object to file, you're completely missing the point. All you need to do is turn the object into something that can be written to file. For example:

// Convert this to a pipe delimited string
const string course::serialize()
{
  stringstream out;

  out<< prefix <<'|'<< number <<'|'<< title <<'|'<< credits <<'|'<< grade;

  return out.str();
}

Then you can do this:

file<< pers.serialize();

Oh, God.... we even did something like this a long time ago, I just didn't think we still wanted to use that method...

I'll whip up an in/out and let you know how it goes ;)

We have all of these nifty new features, but sometimes the oldies work best. ;)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.