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.
- We will use the special file
/dev/null
,
which is always empty.
ifstream in("/dev/null");
if (in.rdstate() & ios::eofbit) cout << "Before, eofbit\n";
if (in.rdstate() & ios::failbit) cout << "Before, failbit\n";
if (in.rdstate() & ios::badbit) cout << "Before, badbit\n";
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.
These are bit masking operations.
.rdstate() returns an integer with, perhaps, several bits set. And-ing
(&
) that value with one particular bit, such as ios::eofbit, will
yield non-zero (true) if that bit is set, and zero (false) if if
isn’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,
a USB drive is faulty, a 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");
if (out.rdstate() & ios::eofbit) cout << "Before, eofbit\n";
if (out.rdstate() & ios::failbit) cout << "Before, failbit\n";
if (out.rdstate() & ios::badbit) cout << "Before, badbit\n";
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);
Another method,
ios::setstate(), sets sets only a particular bit.
// I want to set failbit=1, and leave the other bits unchanged.
cin.setstate(ios::failbit);
ios::clear() and ios::setstate() are useful when writing your own
operator>>
input operator for your own class. Sometimes you
want to indicate a failure, or forgive a failure and try again.
Interrogating bits
The previous slides have been interrogating the bits in a
straightforward, but clumsy fashion. There are several other ways:
How
| Results
|
---|
stream.rdstate()
| ios::eofbit|ios::badbit|ios::failbit
|
stream.good()
| true iff no bits are set
|
stream.eof()
| true iff ios::eofbit is set
|
stream.bad()
| true iff ios::badbit is set
|
stream.fail()
| true iff ios::badbit or ios::failbit is set 😲 🤯
|
! stream
| .fail()
|
stream in boolean context
| ! .fail()
$ cat /proc/uptime
3983868.88 47110206.32
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: 3.98387e+06
Read a number: 4.71102e+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
3983868.89 47110206.37
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: 3983868
Bad data!
|