Show Lecture.IosStateFlags as a slide show.
CS253 Ios State Flags
Overview
- By default, streams do not throw errors when things go bad.
- Though you can enable that if you wish.
- Instead, they maintain several bits (flags)
that indicate the state of the stream.
- Several methods exist which interrogate the stream’s state.
ios State Flags
- An ios (Input/Output Stream) object maintains state.
- Several sorts of things can go wrong, so three different types
of errors are maintained:
- Yes, “fail” and “bad” are quite similar words. Life is difficult.
Summary of flags
ios::eofbit
- ios::eofbit is set when end-of-file
is encountered.
- That’s when you try to read, but can’t, because there’s no more data.
It doesn’t predict.
- Let’s try reading from the special file
/dev/null
,
which is always empty.
ifstream in("/dev/null");
char c;
in.get(c);
if (in.rdstate() & ios::eofbit) cout << "After, eofbit\n";
if (in.rdstate() & ios::failbit) cout << "After, failbit\n";
if (in.rdstate() & ios::badbit) cout << "After, badbit\n";
After, eofbit
After, failbit
eofbit ≡ hit eof trying to read;
failbit ≡ tried to get a char and couldn’t.
ios::failbit
- ios::failbit is set when an I/O operation fails—you didn’t
get what you wanted.
- ios::failbit was set in the previous example, because we
tried to read a character, and couldn’t.
- Let’s try to read a float from
/etc/hostname
,
which has contains the name of this computer,
“beethoven”
(not a number).
ifstream in("/etc/hostname");
float f;
in >> f;
if (in.rdstate() & ios::eofbit) cout << "After, eofbit\n";
if (in.rdstate() & ios::failbit) cout << "After, failbit\n";
if (in.rdstate() & ios::badbit) cout << "After, badbit\n";
After, failbit
ios::failbit
upon open failure
Also, ios::failbit is set when a file can’t be opened.
ifstream in("/this/file/doesn’t/exist");
if (in.rdstate() & ios::eofbit) cout << "eofbit\n";
if (in.rdstate() & ios::failbit) cout << "failbit\n";
if (in.rdstate() & ios::badbit) cout << "badbit\n";
failbit
ios::badbit
- ios::badbit is not set upon a mere conversion error,
but, rather, when a low-level I/O error occurs:
the system runs out of memory, the disk becomes full, etc.
- Let’s try writing to the special file
/dev/full
, which simulates a
disk that is always full.
ofstream out("/dev/full");
out << "foo" << endl;
if (out.rdstate() & ios::eofbit) cout << "After, eofbit\n";
if (out.rdstate() & ios::failbit) cout << "After, failbit\n";
if (out.rdstate() & ios::badbit) cout << "After, badbit\n";
After, badbit
The bits are sticky
ifstream in("/etc/hostname");
double d;
in >> d;
if (in.rdstate() & ios::failbit) cout << "#1: failbit\n";
char c;
in >> c;
if (in.rdstate() & ios::failbit) cout << "#2: failbit\n";
#1: failbit
#2: failbit
- Why doesn’t the second read succeed? Because the bits are sticky.
- Once ios::failbit is set, it stays set.
- The second extraction gives up without trying,
because ios::failbit is set.
Clearing bits
ifstream in("/etc/hostname");
double d;
in >> d;
if (in.rdstate() & ios::failbit) cout << "#1: failbit\n";
char c;
in >> c;
if (in.rdstate() & ios::failbit) cout << "#2: failbit\n";
in.clear();
in >> c;
if (in.rdstate() & ios::failbit) cout << "#3: failbit\n";
cout << "c=‘" << c << "’\n";
#1: failbit
#2: failbit
c=‘b’
ios::clear() clears ios::badbit, ios::eofbit, and ios::failbit.
Setting bits
- Despite its name, if you give
ios::clear() an argument, it sets the state of a stream
to exactly those bits:
// I want to set failbit=1, eofbit=0, badbit=0:
in.clear(ios::failbit);
// I want to set failbit=1, and leave the other bits unchanged.
in.setstate(ios::failbit);
Interrogating bits
The previous slides have been interrogating the bits in a straightforward,
but clumsy fashion. There are several other ways:
$ cat /proc/uptime
1693775.30 20036135.73
The fail() method is sometimes misused:
ifstream in("/proc/uptime");
for (float f; in >> f; )
cout << f << '\n';
if (in.fail())
cerr << "failure!\n";
1.69378e+06
2.00361e+07
failure!
Why did it fail?
Because the final read in the loop failed. If no reads in the loop
failed, then we’d still be in the loop. So, this call to
fail() always succeeds—not useful.
Advice
I generally only use boolean context, and occasionally eof() when
I want to be rigorous. I also avoid .is_open()
:
ifstream in("/etc/hostname");
if (!in) {
cerr << "Can’t open /etc/hostname for reading\n";
return 1;
}
for (float f; in >> f; )
cout << "Hooray, got a number: " << f << '\n';
// All done reading. Why did we stop?
// Was it end-of-file, or did something go wrong?
// Don’t check .fail(), that will always be true at this point.
if (!in.eof())
cerr << "Error!\n";
Error!