I can't understand why fstream sets the failbits when the file you try to open does not exist. I ran into a strange problem today related to fstream in that if the failbit is set it's easy to run into an infinite loop:
| 1 | fstream fin; |
| 2 | fin.open("C:\some_file_that_does_not_exist.txt"); |
| 3 | if(!fin.is_open()) |
| 4 | { |
| 5 | fin.open("C:\some_file_that_exists.txt"); |
| 6 | } |
| 7 | |
| 8 | if(fin.is_open()) |
| 9 | { |
| 10 | string tmp; |
| 11 | while(!getline(fin, tmp).eof()) |
| 12 | { |
| 13 | // Infinite loop! |
| 14 | } |
| 15 | } |
The problem is that the first fin.open failed which set the failbit while the second fin.open succeeded but did not reset the failbits. When you call getline it will never be able to read and therefore never hit the eof.
Simple fix is to add fin.clear() before getline but it's not evident by the logic above that you must do that. Shouldn't fstream reset the failbits if open succeeds?
I use a different check to see if the fstream is valid. Instead of using fstream.is_open() , I use either a boolean test with operator ! or a call to the fstream.good() function.
| 1 | ofstream log_file(".\\program_log.txt"); |
| 2 | |
| 3 | // boolean |
| 4 | if (!log_file) {// couldn't open log properly , operator ! does same thing as ios::fail() |
| 5 | // decide whether to continue without log |
| 6 | } |
| 7 | // good() method |
| 8 | if (!(log_file.good())) { |
| 9 | // see what the problem with the file is |
| 10 | if (log_file.fail()) {// if fail() returns true then either failbit set or badbit set |
| 11 | if (!log_file.bad()) { |
| 12 | // fail bit set for log file |
| 13 | /// Try clearing state and repositioning stream |
| 14 | log_file.clear(); |
| 15 | log_file.seekg(0 , ios::beg); |
| 16 | log_file.seekp(0 , ios::beg); |
| 17 | } else { |
| 18 | // bad bit set for log file , and perhaps fail bit also |
| 19 | // bad bit means inoperable as far as I know , so quit |
| 20 | } |
| 21 | |
| 22 | } else {// if good() is false and if fail() is false , then eof() should be true |
| 23 | // just need to stop reading or reposition place in stream |
| 24 | // have to call clear to get rid of the eofbit state though |
| 25 | log_file.clear(); |
| 26 | log_file.seekg(0 , ios::beg); |
| 27 | } |
| 28 | } |
It makes sense that if you open a new fstream that the failbit shouldn't be set since it hasn't actually failed , I think you're right about that.
- Append -
Actually , though , I understand why the failbit is set when opening a file doesn't work. You wouldn't want ios::good() to return true when a file failed to open would you? So I suppose it's their reasoning that the failbit should be cleared manually by the user but I still agree that if open is called , it should reset the bits itself. Assuming that they would use the ios::setstate function to set the state of the stream after attempting to open a file , then it would make sense that they would use ios::setstate(goodbit) if successful , but maybe they don't. Should be easy to test with something like :
bool file_error_flag = false; fstream file; file.open(".\\nonexistentfile.txt"); if (file.good()) { cout << "File stream incorrectly reported as good when opening a non existent file" << endl; } else { file_error_flag = true; } file.open(".\\realfile.txt"); if (file_error_flag) { if (!file.good()) { cout << "File stream incorrectly reported as not good() when opening an actual file" << endl; } }
Simple fix is to add fin.clear() before getline but it's not evident by the logic above that you must do that. Shouldn't fstream reset the failbits if open succeeds?
It might be considered a "sticky bit" that you must explicitly clear. Then you could detect errors indefinitely later, like glError() or fxam do.
[EDIT] glError() clears them as you fetch the errors.
Actually , though , I understand why the failbit is set when opening a file doesn't work. You wouldn't want ios::good() to return true when a file failed to open would you?
I agree that the failbit should be set when open() fails. But I think what Steve Terry is saying is that when the second open() succeeds it should unset the failbit. It is a new file, essentially a new instance of the whole fstream thing, and nothing has gone wrong yet - so the failbit should have been unset.