Show Lecture.IOStreams as a slide show.
CS253 I/O Streams
Predefined Streams
There are four predefined streams. Don’t open or close them.
Just use them.
- cin: standard input
- cout: standard output (for normal output)
- cerr: standard error (for error output)
- clog: standard error (for error output)
cerr is unbuffered, clog is buffered.
Use cerr for error messages, clog if you use standard error
for logging purposes.
Formatted output:
The insertion operator, <<
, is used for output.
You may recognize it as the left shift operator. It’s that, too.
Isn’t operator overloading wonderful?
int i = 5<<4;
double d = 4.5;
char c = 'x';
const char *ccs = "My dog";
string s = " has fleas";
cout << i << ' ' << d << " " << c << "\n"
<< ccs << s << '\n';
80 4.5 x
My dog has fleas
How can you tell if <<
means bit shift or insertion?
If the left operand is an output stream (or the result of another insertion)
then it’s insertion.
endl
cout << "alpha\n";
cout << "beta" << endl;
cout << "gamma" << "\n";
cout << "delta" << '\n';
alpha
beta
gamma
delta
- Some C++ programmers think that endl and
\n
are synonymous.
They are not.
\n
means: Add a newline ('\n') to the output.
- endl means: Add a newline to the output, and flush the output
buffer. Terminal output gets flushed automatically upon newline, when
the output buffer gets full, and when the program ends. That’s almost
always good enough.
- Don’t use endl unless you really want to flush.
It’s not free, and it confuses those who know what it does.
endl demonstration
static int zero = 0; // fool the optimizer
cout << "Invisible Man\n";
cout << 1/zero << '\n';
SIGFPE: Floating point exception
The Invisible Man was buffered (slide output really goes to a file)
and so was lost when the program terminated abnormally.
static int zero = 0;
cout << "Frankenstein" << endl;
cout << 1/zero << '\n';
Frankenstein
SIGFPE: Floating point exception
Division by zero is undefined behavior, so none of this is guaranteed.
Formatted input
The extraction operator, >>
, is used for input.
You may recognize it as the right shift operator. It’s that, too.
Isn’t operator overloading wonderful?
int i;
cin >> i; // attempt to read an integer
if (cin) // Is the stream in a happy state?
cout << "Read i=" << i << '\n';
else
cout << "Couldn’t read an int: bad data, or end-of-file\n";
-or-
int i;
while (cin >> i)
cout << "Read i=" << i << '\n';
Chaining
Input may be chained, just like output:
int a, b, c;
if ((cin>>a) && (cin>>b) && (cin>>c))
cout << "a=" << a << " b=" << b << " c=" << c << '\n';
-or-
int a, b, c;
if (cin >> a >> b >> c)
cout << "a=" << a << " b=" << b << " c=" << c << '\n';
The &&
forces left-to-right evaluation, so the numbers
are read in the proper order.
Read an entire line
Consider this small file:
$ cat ~cs253/pub/ducks
Huey (red)
Dewey (blue)
Louie (green)
To read an entire line, use getline():
const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
for (string line; getline(in, line); )
cout << "▻" << line << '\n';
▻Huey (red)
▻Dewey (blue)
▻Louie (green)
Beware
There are two versions of getline():
Read a string
Consider this small file:
$ cat ~cs253/pub/ducks
Huey (red)
Dewey (blue)
Louie (green)
Extracting a string via >>
only reads a whitespace-delimited string.
const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
string s;
while (in >> s)
cout << "▻" << s << '\n';
▻Huey
▻(red)
▻Dewey
▻(blue)
▻Louie
▻(green)
Read a character
Consider this small file:
$ cat ~cs253/pub/ducks
Huey (red)
Dewey (blue)
Louie (green)
To read a raw char, without skipping whitespace, use istream::get():
const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
char c;
while (in.get(c))
cout << "▻" << c;
▻H▻u▻e▻y▻ ▻(▻r▻e▻d▻)▻
▻D▻e▻w▻e▻y▻ ▻(▻b▻l▻u▻e▻)▻
▻L▻o▻u▻i▻e▻ ▻(▻g▻r▻e▻e▻n▻)▻
Unlike getline() in the previous slide, istream::get() is a method.
Read a character
Consider this small file:
$ cat ~cs253/pub/ducks
Huey (red)
Dewey (blue)
Louie (green)
Extracting a char via >>
only reads a whitespace-delimited character,
which is rarely useful. That is, it skips whitespace.
const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
char c;
while (in >> c)
cout << "▻" << c;
▻H▻u▻e▻y▻(▻r▻e▻d▻)▻D▻e▻w▻e▻y▻(▻b▻l▻u▻e▻)▻L▻o▻u▻i▻e▻(▻g▻r▻e▻e▻n▻)
Unreading
$ cat ~cs253/pub/ducks
Huey (red)
Dewey (blue)
Louie (green)
To put a character back, use istream::unget():
const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
char c;
in.get(c);
cout << "First character: " << c << '\n';
in.unget(); // no argument
string s;
in >> s;
cout << "First string: " << s << '\n';
First character: H
First string: Huey
Peeking
$ cat ~cs253/pub/ducks
Huey (red)
Dewey (blue)
Louie (green)
To look at the next char without consuming it, use istream::peek():
const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
char c = in.peek();
cout << "First character: " << c << '\n';
string s;
in >> s;
cout << "First string: " << s << '\n';
First character: H
First string: Huey
Stream state
- A stream is put into an error state if something, such as reading
a number, goes wrong, including EOF (end-of-file).
- Test the state by evaluating the stream as an boolean value:
happy=true, error=false.
- To tell the difference between bad input and end-of-file,
use istream::eof().
Don’t do this:
% # This file is only one line.
% cat /etc/hostname
beethoven
% wc -l /etc/hostname
1 /etc/hostname
ifstream in("/etc/hostname");
while (!in.eof()) { // BAD! 😖🙀
string s;
getline(in, s);
cout << "▻" << s << '\n';
}
▻beethoven
▻
- istream::eof() doesn’t ask “Will the next read hit end-of-file?”
- Instead, istream::eof() asks “Did we already hit end-of-file?”
- That is, it doesn’t predict what the next read will do.
- It tells you what the previous read did.
The better way to do it
% cat /etc/hostname
beethoven
ifstream in("/etc/hostname");
string s;
while (getline(in, s))
cout << "▻" << s << '\n';
▻beethoven
Don’t try to predict the future. Just ask for a line,
and stop when the answer is “no”.
Same for numbers
% cat /proc/uptime
1762406.15 20847905.45
ifstream in("/proc/uptime");
double d;
while (in >> d)
cout << "▻" << d << '\n';
▻1.76241e+06
▻2.08479e+07
Now with error checking!
% cat /proc/uptime
1762406.16 20847905.52
ifstream in("/proc/uptime");
double d;
while (in >> d)
cout << "▻" << d << '\n';
// Did we stop due to EOF, or bad data?
if (!in.eof())
cerr << "😠\n";
▻1.76241e+06
▻2.08479e+07
Now with error checking!
% cat /proc/uptime
1762406.17 20847905.57
ifstream in("/proc/uptime");
if (!in) { // Is the stream in a bad state?
cerr << "Can’t open /proc/uptime\n";
return 1;
}
int n; // OOPS——should be double
while (in >> n)
cout << "▻" << n << '\n';
// Did we stop due to EOF, or bad data?
if (!in.eof())
cerr << "File not full of ints. 😠\n";
▻1762406
File not full of ints. 😠