0

I am so lost. I am having an issue with the following code when saving and loading data to a binary file. I have a very similar/exact piece of code that works with a different object. I think it has something to do with the email address and the @ symbol in it. Here are the important functions.

/* This is my save to file function */
FILE *file=fopen("patronsave","wb");
fwrite(&buffer,sizeof(buffer),1,file);
for(int i=0;i<3;i++){
Patron_Class *ppointer = new Patron_Class();
cout << "\n" << endl;
ppointer->save(file);
}
fclose(file);

That is the function I use to save my objects to a file.

Here is my code that I am using to load the file:

vector<Patron_Class*> Patron_Entries;
FILE *file=fopen("patronsave","rb");
fread(&buffer,sizeof(buffer),1,file);
printf("Buffer: %d\n",buffer);
for(int i=0;i<buffer;i++){
    Patron_Class *pointer2 =new Patron_Class(file);
    Patron_Entries.push_back(pointer2);
    Patron_Entries[i] -> print();
    system("pause");
    }
fclose(file);

If I run the save function and then immediatly run the load function it works, but if I only run the load function it crashes when it tried to load the email. Here is my class and object code:

class Patron_Class{
public:
long patron_id;
string F_name;
string L_name;
long phone_num;
string email;
string street_address;
string city;
string state;
int zip_code;
Patron_Class(){
    cout << "Please enter a new ID" << endl;
    cin >> patron_id;
    cin.ignore(1000, '\n');
    system("cls");
    cout << "Please enter a First name" << endl;
    cin >> F_name;
    cin.ignore(1000, '\n');
    system("cls");
    cout << "Please enter a last name" << endl;
    cin >> L_name;
    cin.ignore(1000, '\n');
    system("cls");
    cout << "Please enter a phone number" << endl;
    cin >> phone_num;
    cin.ignore(1000, '\n');
    system("cls");
    cout << "Please enter a email" << endl;
    cin >> email;
    cin.ignore(1000, '\n');
    system("cls");
    cout << "Please enter a street address" << endl;
    cin >> street_address;
    cin.ignore(1000, '\n');
    system("cls");
    cout << "Please enter a city" << endl;
    cin >> city;
    cin.ignore(1000, '\n');
    system("cls");
    cout << "Please enter a State via it's initials" << endl;
    cin >> state;
    cin.ignore(1000, '\n');
    system("cls");
    cout << "Please enter a zip code" << endl;
    cin >> zip_code;
    cin.ignore(1000, '\n');
    system("cls");
    cout << "You have created a new patron named:   " << F_name << " " << L_name << endl;
}
Patron_Class(FILE *inputfile){
    fread(&patron_id, sizeof(patron_id),1,inputfile);
    fread(&F_name, sizeof(F_name),1,inputfile);
    fread(&L_name, sizeof(L_name),1,inputfile);
    fread(&phone_num, sizeof(phone_num),1,inputfile);
    fread(&email, sizeof(email),1,inputfile);
    fread(&street_address, sizeof(street_address),1,inputfile);
    fread(&city, sizeof(city),1,inputfile);
    fread(&state, sizeof(state),1,inputfile);
    fread(&zip_code, sizeof(zip_code),1,inputfile);
}

void print(){

    cout << patron_id << "    " << F_name << "    " << L_name << "    " << phone_num << "    " << email << "    " << street_address << "    " << city << "    " << state << "    " << zip_code << "\n" << endl;
}

void save(FILE *inputFile){
    fwrite(&patron_id, sizeof(patron_id),1,inputFile);
    fwrite(&F_name, sizeof(F_name),1,inputFile);
    fwrite(&L_name, sizeof(L_name),1,inputFile);
    fwrite(&phone_num, sizeof(phone_num),1,inputFile);
    fwrite(&email, sizeof(email),1,inputFile);
    fwrite(&street_address, sizeof(street_address),1,inputFile);
    fwrite(&city, sizeof(city),1,inputFile);
    fwrite(&state, sizeof(state),1,inputFile);
    fwrite(&zip_code, sizeof(zip_code),1,inputFile);
}


};

Does anyone know why it might be crashing?

Linux4Hope
  • 233
  • 2
  • 8
  • I actually want to learn Ruby....But I am taking a class on C++ ;) – Linux4Hope Mar 02 '13 at 18:48
  • 1
    Couple of things. 1) You're using `FILE*` instead of `std::fstream`, why is that? 2) Why are you using pointers? There's no reason to. 3) You might have forgotten the entire concept of a constructor or a class if you're still reading from `stdin`. – Rapptz Mar 02 '13 at 18:49
  • Any good answer to this question would probably amount to a beginner class in C++, so I can only tell you: [get a good book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). – Etienne de Martel Mar 02 '13 at 18:51
  • I am using FILE* because that is what we had learned in the class, Why am I using pointers? Good question, it seems that is how I could get it to work lol. This code works perfectly fine on my other classes, just not the one with an email address – Linux4Hope Mar 02 '13 at 18:51
  • 1
    Skip class and forget everything you learned about "C++". Your code is written in ancient C++ style (think 80's/early 90's) mixed with Java. It contains memory leaks and is highly unreadable. As Etienne said, get a [good book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). –  Mar 02 '13 at 18:57
  • A good book would be extremly helpfull, if I had the time to go back and re-learn C++ before the end of my class. I only have another week with this class, and I have been struggling with this issue for about 5 days now and still can't figure it out :/ – Linux4Hope Mar 02 '13 at 18:59

1 Answers1

1

This is CLEARLY wrong:

string F_name;

...
fread(&F_name, sizeof(F_name),1,inputfile);

...
fwrite(&F_name, sizeof(F_name),1,inputFile);

[The same applies to all the other strings in your PatronClass - I'm using the first one for this example]

The class std::string will look something like this (for illustration purposes, the exact implementation involves several layers, some templates, and other stuff, so this is simplified for the purposes of the explanation to follow):

class string
{
   char *str;
   int len;

 public: 
   ... 
 };

So, when you do fread from the file and fwrite to the file, you are reading/writing the char *str; and int len; members from/to the file.

Let's say we start your program from scratch, with no data in the file, and we use the Patron_Class() constructor. So we read in an id, and then the F_name from the console. Let's say we enter Charles. So somehow the string class will allocate 8 bytes of memory, at the address of 0x600018. So string::str == 0x600018 and len = 8 - at location 6000018 in the heap are the letters C h a r l e s \0 [ spaces just to illustrate they are in separate memory locations]. Now we save this to a file. So the file contains 00600018 00000008. Now we stop the program and start it again, using the PatronClass(file) constructor. The heap is completely empty. We load the file data, so string::str = 0x600018 and len = 8, but that locaton 0x600018 does not contain C h a r l e s \0, but whatever the heap normally is filed with when the heap is initialized [quite likely 0]. So no wonder your name doesn't appear.

Now, the exact behaviour of your actual program is probably not the same as what I've described above, but it will NOT work right. No way, never, ever. No matter what characters you have or haven't got in names, email addresses or any other string in your code. The only reason it may APPEAR to work is that the data is still, for the most part, there in the heap, and it seems to be working because it doesn't get overwritten by something else in your case.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • Hi Mats, That makes sense! So in order for me to fix this, should I not use strings and use character arrays instead? – Linux4Hope Mar 02 '13 at 21:27
  • Not necessarily, but you need to store the CONTENT of the string in some sensible form not the internal representation of the string. So for example, you could write out the length followed by the c_str(), and then use `resize()` and read into `&F_name[0]` perhaps. – Mats Petersson Mar 02 '13 at 22:17
  • Gotcha, I went around and changed the code up and it worked! Thank you for the explanation! – Linux4Hope Mar 03 '13 at 02:37