Show Lecture.IosStateFlags as a slide show.
CS253 Ios State Flags
Overview
- By default, I/O streams do not throw errors when things go bad.
- Though you can enable that, if you wish, via .exceptions().
- You can program in the style where I/O operations throw
exceptions, caught in your code, instead of checking status.
It seems to be less common than the non-exception style shown in this
course.
- Instead, I/O streams maintain several bits (flags)
that indicate the state of the stream.
- Several methods exist which interrogate the I/O stream’s state.
ios State Flags
Summary of flags
- 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 do something (get a char) and couldn’t.
- ios::failbit is set when an I/O operation fails—you didn’t get what
you wanted. Couldn’t open a file, read a number/char/string, etc.
- 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
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 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" << flush; // Force writing now, not later.
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. It’s pouty.
- 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:
cin.clear(ios::failbit);
// I want to set failbit=1, and leave the other bits unchanged.
cin.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
1764449.38 20872006.37
The .fail() method is sometimes misused:
ifstream in("/proc/uptime");
for (float f; in >> f; )
cout << "Read a number: " << f << '\n';
if (in.fail()) // 🦡
cerr << "failure!\n";
Read a number: 1.76445e+06
Read a number: 2.0872e+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
$ cat /proc/uptime
1764449.38 20872006.43
Use boolean context instead of .is_open() or .fail().
For error checking, use the technique described at the
end of the Introduction to I/O Streams lecture.
ifstream in("/proc/uptime");
if (!in) {
cerr << "Can’t open /proc/uptime for reading\n";
return 1;
}
int v;
while (v=1, in >> v)
cout << "Read a number: " << v << '\n';
// All done reading. Why did we stop?
// Bad data will set the variable to ±∞ or zero.
if (v != 1)
cerr << "Bad data!\n";
Read a number: 1764449
Bad data!