简体   繁体   中英

Why my program doesn't read data from the binary file in C++?

Project Detail :: I have been trying to do a project which i named Expense Management system.

General code Info:: I have a class "expense" it has only two function set and get data.I have a main() function and also inside it i have menu() function.

function menu :: there are 4 cases but only two of them are core function ,case 1:: write data into the file( Expense detail) and case 2 Read the data and display it .

Problem to be fixed :: My below case 2 ,Function code doesn't work ie it doesn't display the content of the file

The Below code is fixed according to the recommendation commented below in Answers ::

    #include<iostream>
    #include<string.h>
    #include<fstream>

    using namespace std;



    class expense{
    public:
        //string
        char  date[20];
       // string 
        char title[20];
        double exp;

    public:

        void set_data(){
            cout<<"\nDate should be in the format\n i.e. day/moth/year eg 12/5/2018\n";
            cout<<"Enter the whole date\n";
            cin>>date;
            cout<<"Give title for expense\n";
            cin>>title;
            cout<<"Enter the total expense\n";
            cin>>exp;
        }

        void get_data(){
            cout<<"\tDate :: ";
            cout<<date;
            cout<<"\tTitle :: ";
            cout<<title;
            cout<<"\tExpense :: ";
            cout<<exp;
        }

    };

//header files 
    void menu();

//global variable's    
    int count=0;
    double tot=0;


//main function 
    int main()
    {
        menu(); //calling function 
        return 0;
    }

//function definition

    void menu(){
        int ch;
        int n;
        int i;
        char choice;
        string dd;
        int flag=0;
        expense exe;
        fstream fp;//file obj
        fp.open("test.dat",ios::app | ios::out | ios::in | ios::binary);
//opening file in different modes 
        if (!fp.good())
            cout << "file error\n";
    //loop below
        do {

            cout << "\n --------------------------------------- \n";
            cout << "Welcome to the Expense Management System" << endl;
            cout << "1.Enter the expense.\n";
            cout << "2.Display all the expenses.\n";
            cout << "3.Find expense for a particular date.\n";
            cout << "4.Display the total expenditure.\n";
            cout << "\nEnter the choice :: \n";
            cin >> ch;
            switch (ch) {
// case 1:: write data into the file 
                case 1:
                    exe.set_data();
                    fp.write(reinterpret_cast<char *>(&exe), sizeof(expense));
                    break;
                case 2:
//case 2 read all the data from the file 
                    fp.seekg(0,ios::beg);
                    cout << "All the expenses are listed below ::\n";
                    fp.read(reinterpret_cast<char *>(&exe), sizeof(expense));
                    exe.get_data();
                    while (fp.read(reinterpret_cast<char *>(&exe), sizeof(expense)))
                    {

                        cout<<"\n";
                        exe.get_data();

                    }
                    break;

                case 3:
//case 3 find the expense data from the file of the particular date
                fp.seekg(0,ios::beg);
                cout<<"Enter the date:\n";
                cin>>dd;
                while (fp.read(reinterpret_cast<char *>(&exe), sizeof(expense)))
                    {
                        if(fp.gcount() != sizeof(exe))
                        {
                        cout << "read error, we didn't get the right number of bytes\n";
                        break;
                        }

                        if((strcmp(exe.date,dd)==0)
                        {
                        flag=1;
                        exe.get_data(); 
                        }
                        cout<<"\n";
                    }
                    if(flag==0){
                    cout<<"Kindly Enter The Correct Date\n";
                    }
                    //fp.close();
                    break;
                case 4:
 //case 4:: calculates the total expense amount
                    fp.seekg(0,ios::beg);            
                    while (fp.read(reinterpret_cast<char *>(&exe), sizeof(expense)))
                    {
                        if(fp.gcount() != sizeof(exe))
                        {
                        cout << "read error, we didn't get the right number of bytes\n";
                        break;
                        }
                        tot+=exe.exp;

                    }
                    cout << "The Total Expenditure is ::\n"<<tot<<endl;
                    //fp.close();
                    break;


            }
            cout<<"\nDo you want to access the Main Menu?(y/n)\n";
            cin>>choice;
        }while(choice =='Y' || choice =='y');
   }

Solution :::

  My problem is finally solved.I have drawn a conclusion since the program is working fine.

    Things to be fixed in the above program ::-

    1)While working with file.write()  and file.read() function  we should not include string variable so i changed every string variable into character array(IE each variable of fixed then no need to allocate memory additionally )

    Reason ::std::string does not fulfill the TriviallyCopyable requirement because, in order to hold strings of arbitrary length, it is allocating additional memory dynamically. This memory will not be part of the string's object representation and thus also not part of the object representation you acquire with the reinterpret_cast(thanks to eukaryota )

    2) I should reset the pointer to the start of the file every time before reading data from the file.( thanks to kerrytazi )
    E.G::
    fp.write(...);
    fp.seekp(0, std::ios::beg); /* set file pointer at the start of file */
    fp.read(...);

    3)Yes i have use the " reinterpret_cast<char *> " and it works cause i have used "char array" data type.

    4)Instead of writing a binary file we can also serialize a object in C++ and write data into text file also.
link :: https://thispointer.com/c-how-to-read-or-write-objects-in-file-serializing-deserializing-objects/


5)TO read multiple  data from a file using while loop will not work if we use while( !file.eof()) which is explained by the link below.
link :: https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong
fp.write(reinterpret_cast<char *>(&exe), sizeof(expense));
fp.read(reinterpret_cast<char *>(&exe), sizeof(expense))

These will absolutely not do what you want to do here. There are only very few specific cases where this is works and even then the files written in this manner will not be portable.

The pointer obtained from reinterpret_cast<char*>(&exe) will point to the address of the object exe and reading from there will give you the object representation of the object exe , basically the actual bytes stored at that location in memory, which is not necessarily enough information to reconstruct the full state of the object.

Writing the byte sequence to a file and reloading it by reading from the file with the second code line will only be sufficient to reconstruct exe if exe has a TriviallyCopyable type. Informally, this concept requires, in addition to other things, that all data required to reconstruct the object state is located in the object's storage itself and not in another memory location linked via eg a pointer member. In your case std::string does not fulfill the TriviallyCopyable requirement because, in order to hold strings of arbitrary length, it is allocating additional memory dynamically. This memory will not be part of the string 's object representation and thus also not part of the object representation you acquire with the reinterpret_cast .

You should probably never use reinterpret_cast if you are not 100% sure what it does.

You need to serialize the expense structure properly and write it to the file in a format of your choosing. This is a non-trivial task if you want to do it properly. If you want to do it roughly, assuming there are no whitespaces in the strings, then:

fp << exe.date << exe.title << exe.exp << "\n";

might be a way to do it.

You need to move your file pointer back after writing to it.

fp.write(...);
fp.seekp(0, std::ios::beg); /* set file pointer at the start of file */
fp.read(...);

How it works:

/* file:       */
/*       ^     */

fp.write("test", 4);

/* file: test  */
/*       ----^ */

fp.seekp(0, std::ios::beg);

/* file: test  */
/*       ^     */

fp.read(outBuffer, 4);

/* file: test  */
/*       ----^ */

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM