CS253: Software Development with C++

Fall 2020

IO Streams

Show Lecture.IOStreams as a slide show.

CS253 IOStreams

Predefined Streams

There are four predefined streams. Don’t open or close them. Just use them.

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?

endl demonstration

static int zero = 0;
cout << "Alpha" << '\n' << 1/zero << '\n';
SIGFPE: Floating point exception
static int zero = 0;
cout << "Beta" << '\n';
cout << 1/zero << '\n';
SIGFPE: Floating point exception
static int zero = 0;
cout << "Gamma" << endl;
cout << 1/zero << '\n';
Gamma
SIGFPE: Floating point exception
static int zero = 0;
cout << "Delta" << endl << 1/zero << '\n';
Delta
SIGFPE: Floating point exception

Division by zero is undefined behavior, so none of this is guaranteed.

endl

cout << "alpha\n";
cout << "beta" << endl;
cout << "gamma" << "\n";
cout << "delta" << '\n';
alpha
beta
gamma
delta

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';

-or-

    if (!(cin >> i))	    // Could we read?
        cerr << "Input failed!\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

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
► 

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
1696620.79 20069784.07
ifstream in("/proc/uptime");
double d;
while (in >> d)
    cout << "► " << d << '\n';
► 1.69662e+06
► 2.00698e+07

Now with error checking!

% cat /proc/uptime
1696620.80 20069784.14
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.69662e+06
► 2.00698e+07

Now with error checking!

% cat /proc/uptime
1696620.81 20069784.27
ifstream in("/proc/uptime");
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 << "😠\n";
► 1696620
😠