Fahad Ur Rehman
Fahad Ur Rehman

Reputation: 93

C++: Problems reading input from a binary file

I have a class AccountManagement in AccountManagement.cpp. I have another class called Account in Account.cpp. I have a template that Orders the given data inside the list using OrdereList class, which also has it's own iterator. The AccountManagement class outputs the Accounts list in a binary file as shown below:

void AccountManagement::saveData(const char * file) //saves data in the specified binary file
{
    ofstream out(file, ios::out | ios::binary);
    if(!out)
    {
        cerr<<"Problem opening output file!"<<endl;
    }   
    OrderedList<Account>::iterator it = this->account_manager.begin();
    for(int i = 0; i < this->total_accounts; i++)
    {
        Account temp = *it;
        out.write((char*)&temp, sizeof(Account));
        it++;
    }
    out.close();
}

I have defined a following function inside AccountManagement class that reads all the data from binary file and outputs it. This function works perfectly fine. It is shown here:

void AccountManagement::output()
{
    ifstream in("accounts.dat", ios::in | ios::binary);
    if(!in)
    {
        cerr<<"File doesn't exist!"<<endl;
        exit(1);
    }
    Account acc;
    while(in.read((char*)&acc, sizeof(Account)))
    {
        cout<<acc;
    }
    in.close();
}

However, when I use this same function (with different name) in another file, which has Account.h header file as well to retrieve data from the same "account.dat" file it gives me segmentation fault. What could be the problem? Following is the function:

void loadData()
{
    ifstream in("accounts.dat", ios::in | ios::binary);
    if(!in)
    {
        cerr<<"File doesn't exist!"<<endl;
        exit(1);
    }

    Account acc;
    while(in.read((char*)&acc, sizeof(Account)))
    {
        cout<<acc;
    }
    in.close();
}

Account's class declaration:

class Account 
{
    friend ostream& operator<<(ostream&,const Account&); //overloading << operator
    friend istream& operator>>(istream&,Account&); //overloading >> operator
    public:
        void operator=(const Account&); //overloading = operator
        bool operator<=(const Account&); //overloading <= operator
        bool operator<(const Account&); //overloading < operator

    private:
        string number; //Account Number
        char name[100]; //Account holder's name
        char sex; //M or F indicating the gender of account holder
        MYLIB::Date dob; //date of birth of account holder
        char address[100]; //address of account holder
        char balance[20]; //balance of account holder
};

Upvotes: 0

Views: 132

Answers (2)

Christophe
Christophe

Reputation: 73587

Your function AccountManagement::output() gives the impression it works perfectly, if you save the object and load it again in the same object and provided the string hasn't changed in the meantime.

What's wrong ?

As soon as your object is no longer a POD object (i.e. it contains data that use pointers, or use virtual functions, etc...), you can't just save it just by writing its memory to the disk.

In your case, the second function fails for this reason. The first function only gives the impression that it works. The string is a complex object that stores somewhere pointers to dynamically allocated memory. If you write the object and read it back as you did, without changing the object, the values that are in memory are simply re-read. The value of the hidden pointer that is read is exactly what it was before the read. That's a very lucky situation. But in most cases it will fail.

How to solve it ?

To save your object, you should serialize it: write/reade each component to the file separatly, using an appropriate function.

THe easiest way to do this is to use some existing serialisation libraries, such as boost serialization.

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409442

I don't know about the MYLIB::Date class, but it's enough that you have a std::string object in there.

The std::string object allocates memory dynamically to fit the string it contains. And memory allocated on the heap is available only to the current process, you can't save a pointer (which is inside the std::string class) and load it from some other process and hope there will be valid memory at that pointer.

If you save a pointer to dynamically allocated memory in one process, and load and use it from another process then you will have undefined behavior.

You need to serialize the string in order to save it. Possible the MYLIB::Data object as well.


Disclaimer: It will work on small embedded systems with a single unified address map, unfortunately all the bid user-oriented operating systems (like Windows, OSX and Linux) have separate address-spaces and walls between processes.

Upvotes: 3

Related Questions