Andrew
Andrew

Reputation: 45

Weird ifstream behaviour

Writing code to write object into a file, read them, and print on the screen

#include<iostream>
#include<fstream>
#include<cstring>
using namespace std;
////////////////////////////////////////////////////////////
class employee{
    private:
        string name;
        unsigned long ID;
    public:
        employee():name(""),ID(0){}
        void putdata(){cout<<"Enter employee's name: ";cin>>name;cout<<"Enter employee's ID: ";cin>>ID;}
        void getdata(){cout<<"Employee's name is: "<<name<<endl;cout<<"Employee's ID is: "<<ID<<endl;}
        friend ostream& operator << (ostream&,employee&);
        friend istream& operator >> (istream&,employee&);
};

ostream& operator << (ostream& f,employee& emp){ // запись объекта в файл
    f<<emp.ID<<"-"<<emp.name<<"?";
    return f;
}

istream& operator >> (istream& f,employee& empl){ // чтение объекта из файла
    char dummy;
    f>>empl.ID>>dummy;
    f>>dummy;
    empl.name="";
    while(dummy!='?'){
        empl.name+=dummy;
        f>>dummy;       
    }
}
////////////////////////////////////////////////////////////
int main(){
    char ch='y';
    ofstream file1;
    ifstream file2;
    file1.open("TAB.txt");
    employee one;
    while(ch=='y'){ // цикл для записи
        one.putdata();
        file1<<one; // write data into file
        cout<<"Go on?(y,n): ";cin>>ch;
    }
    file1.close();
    file2.open("TAB.txt");
    while(file2.good()){ // цикл для чтения из файла
        file2>>one;
        one.getdata();      
    }
    file2.close();
    system("pause");
    return 0;
}

After entering objects, th program print them out and hanging. I guess there is a problem with while(file2.good()) but I'm not sure. Can anyone comment?

Upvotes: 0

Views: 266

Answers (2)

Andrew
Andrew

Reputation: 45

It finally works(I can't belive):

#include<iostream>
#include<fstream>
#include<cstring>
using namespace std;
////////////////////////////////////////////////////////////
class employee{
    private:
        string name;
        unsigned long ID;
    public:
        employee():name(""),ID(0){}
        void putdata(){cout<<"Enter employee's name: ";cin>>name;cout<<"Enter employee's ID: ";cin>>ID;}
        void getdata(){cout<<"Employee's name is: "<<name<<endl;cout<<"Employee's ID is: "<<ID<<endl;}
        friend ostream& operator << (ostream&,employee&);
        friend istream& operator >> (istream&,employee&);
};

ostream& operator << (ostream& f,employee& emp){ // запись объекта в файл
    f<<emp.ID<<"-"<<emp.name<<"?";
    return f;
}

istream& operator >> (istream& f,employee& empl){ // чтение объекта из файла
    char dummy;
    f>>empl.ID>>dummy;
    f>>dummy;
    empl.name="";
    while(dummy!='?'){
        empl.name+=dummy;
        f>>dummy;       
    }
}
////////////////////////////////////////////////////////////
int main(){
    char ch='y';
    ofstream file1;
    ifstream file2;
    file1.open("TAB.txt");
    employee one;
    while(ch=='y'){ // цикл для записи
        one.putdata();
        file1<<one; // write data into file
        cout<<"Go on?(y,n): ";cin>>ch;
    }
    file1.close();
    file2.open("TAB.txt");
    while(file2.good()&&file2.peek()!=EOF){ // цикл для чтения из файла

        file2>>one;
        one.getdata();      
    }
    file2.close();
    system("pause");
    return 0;
}

I've added file2.peek()!=EOF condition

Upvotes: 0

Loki Astari
Loki Astari

Reputation: 264669

The thing you have missed is that EOF is not set until you read past the end of file. The last successful read will read up-to (but not past) the end of file and thus EOF is false and good() is true but there will be no more data on the stream (so the next read will fail).

This is why you need to check that a read from a stream succeeds. To aid in this when you use a stream object in a boolean context (while/if test) it converts itself to boolean the value being the state of the stream.

 if (stream) {
      std::cout << "stream is in good state\n";
 }

Also the input operator>> returns a stream (so it can be chained) so the result of a read can also be used in a test.

 if (stream >> value) {
      std::cout << "The read worked.\n";
 }

Your problem is you are not correctly checking the state of the input stream after reading.

istream& operator >> (istream& f,employee& empl)
{
    char dummy;
    f>>empl.ID>>dummy;  // Did this work?
                        // You did not test the state of `f` after the read.
    f>>dummy;
    empl.name="";
    while(dummy!='?'){
        empl.name+=dummy;
        f>>dummy;          // Again if this read fails
                           // because you reached the end of file
                           // then the variable 'dummy' is not updated
                           // then this will go into an infinite loop.
    }
}

Every read should check the state of the stream.

std::istream& operator>>(std::istream& f, employee& empl)
{
    // The other standard thing is that if the read fails.
    // then you should not alter the state of your object.
    // so read into a temporary object.
    char dummy;


    employee tmp;
    if (f >> tmp.id >> dummy && dummy == '-')
    {
        // We have correctly read the ID (and following '-' char)
        // Note: The output operator outputs '-' after the ID.
        // Now we try and read the name.

        // While the read of the character worked.
        // and the character is not '?' keep adding it to
        // name. If we reach then end of the file this
        // loop will exit.
        while(f >> dummy && dummy != '?')
        {
           tmp.name += dummy;
        }

        // If the stream is still good.
        // then update the object.
        // If the stream is not good then something went wrong
        // we reached the end of file before we found the '?'
        if (f)
        {
            using std::swap;
            swap(tmp, empl);
        }
    }
    return f;
}

Now that the input operator is good. We can correctly write the output loop.

while(file2.good()){
    file2>>one;
    one.getdata();      
}

The problem with this is that file2>>one may fail. You should test for that.

while(file2.good()){
    if (file2 >> one) {
        one.getdata();     
    } 
}

But to do this correctly put the read as the condition of the while. Then you don't even enter the loop if the read fails.

while(file2 >> one)
{
    one.getdata();      
}

Upvotes: 1

Related Questions