Streams
I/O is not built into the C++ language. Instead, it is provided in the standard library. To get the I/O facilities, you will need some of the following include directives in your program:
#include <iostream> // standard streams
#include <fstream> // file streams
#include <sstream> // string streams
using namespace std;
I/O sources and destinations-- devices, strings, and files—are represented in C++ programs by objects called streams. A stream can be thought of as a sequence of characters. An input stream is a sequence of characters coming from an input device. An output stream is a sequence of characters heading for an output device. When a program needs input, it extracts characters from an input stream. When a program wants to perform output, it inserts characters into an output stream.
Output Streams
Output streams are instances of the class ostream, which is defined in <iostream>. There are several pre-defined output streams:
ostream cout; // standard output stream
ostream cerr, clog; // insert error messages here
Initially, all three of these streams are associated with the monitor, but file redirection can reassociate cout with a file or the standard input stream of another program. cerr and clog only differ in their buffering strategies.
The most basic service provided by instances of the ostream class inserts a single character:
cout.put('c');
cout.put('a');
cout.put('t');
cout.put('\n');
We can insert a string using the write function:
cerr.write("cat\n", 4); // write 4 chars
Unfortunately, put() and write() only deal with characters. They do not perform translation from binary values to strings. The standard library provides translators that do this. All output translators are represented by the left shift operator. For example:
cout << 42; // translates int to string, then inserts
cout << 3.1416; // translates double to string, then inserts
Because of overloading, this doesn't cause confusion. The C++ compiler can tell by the left operand, cout, that translation, not left shift is to be performed. The right operand tells it what type of translation is to be performed: int to string, double to string, etc.
We can even use translators to write ordinary strings and characters:
cout << "cat";
cout << '\n';
Translators return their left operand as a value, thus several output operations can be chained together on a single line:
cout << "result = " << 6 * 7 << '\n'; // prints "result = 42"
Because left shift is left-to-right associative, this is the same as:
(((cout << "result = ") << 6 * 7) << '\n');
Which is the same as:
cout << "result = ";
cout << 6 * 7;
cout << '\n';
We can even define our own translators. Assume rational numbers (i.e. fractions like 2/3, 1/8, and 7/5) are represented by two ints (numerator and denominator) encapsulated in a struct:
struct Rational
{
int num, den;
// etc.
};
Here are declarations of the rational numbers 3/8 and 2/1:
Rational q = {3, 8}, p = {2, 1};
By defining our own variant of the left shift operator:
ostream& operator<<(ostream& os, const Rational& rat)
{
os << rat.num;
if (rat.den != 1)
os << '/' << rat.den;
return os;
}
our rational numbers will be translated into strings and printed according to our desire:
cout << q << '\n'; // prints 3/8
cerr << p << '\n'; // prints 2
Note: we must pass os by reference, because it is modified by having the numerator and denominator inserted into it. To allow chaining, we must also return os. Also note that os << rat.num and os << rat.den are calling the integer variant of left shift. They are not recursively calling the variant being defined.
Output File Streams
Writing to a file is easy, too. Simply declare an instance of the ofstream class. The constructor expects one input: the name of the file to open or create. The fstream class is derived the ostream class:
So all of the operations discussed above, including left shift, can be used with file streams. For example, this piece of code simply writes some strings and integers to a file called "data1":
ofstream fs("data1");
fs << "Here are some odd numbers:\n";
for(int i = 0; i < 20; i++)
fs << 2 * i + 1 << '\n';
fs << "Here are some even numbers:\n";
for(i = 0; i < 20; i++)
fs << 2 * i << '\n';
Assume Employee is declared as follows:
struct Employee
{
Employee(string n, string ss, int ded, char gen, double sal)
{
name = n;
ssn = ss;
deductions = ded;
gender = gen;
salary = sal;
}
string name, ssn;
int deductions;
char gender;
double salary;
};
As we did with rationals, we provide an Employee variant of the left shift operator:
ostream& operator<<(ostream& os, const Employee& e)
{
os << "Name = " << e.name << '\n';
os << "SSN = " << e.ssn << '\n';
os << "# of deductions = " << e.deductions << '\n';
os << "gender = " << e.gender << '\n';
os << "salary = " << e.salary << '\n';
return os;
}
Next, declare a few employees:
Employee
smith("Smith", "111-11-1111", 4, 'M', 25064.00),
jones("Jones", "123-45-6789", 1, 'F', 27333.33),
wong("Wong", "222-22-2222", 3, 'F', 17953.700);
As you would expect, this fragment of code writes the three employees to a file caled "data2".
ofstream fs("data2");
fs << smith << '\n';
fs << jones << '\n';
fs << wong << '\n';
You can use a text editor or word processor to inspec these files.
Output String Streams
The translators are handy. Too bad there are no translators for strings. But like files and the monitor, strings can be sinks for a class of streams called output string streams. Like ofstream, the ostringstream class derived from ostream:
Any value that defines a variant of left shift can use the translators to change itself into a string:
template <class Value>
string toString(const Value& val)
{
ostringstream os;
os << val; // hope << is defined for Value
return os.str();
}
Here is a patch of code that uses this template:
string s1 = "the answer is";
string s2 = toString(6 * 7);
string s3 = s1 + " = " + s2;
cout << s3 << '\n';
Input Streams
Output is easy, but input is filled with pitfalls that await the unwary.
Input streams are instances of the istream class. An ifstream and istringstream class are derived from these:
The standard input, mapped to the keyboard by default, is defined in <iostream>:
istream cin; // standard input
The basic service provided by an input stream extracts a single character:
char c = cin.get();
Another version takes a character reference as a parameter:
cin.get(c);
Of course passing a constant to get fails:
cin.get(6 * 7); // error, no place to put the character!
Most streams buffer their characters, so it's possible to put an extracted character back into the input stream:
cin.putback(c);
Or we can simply peek at the next character in the input stream:
cin.peek();
This is equivalent to:
char temp = cin.get();
cin.putback(temp);
Peek is useful when we need to know if the input stream contains a number before we attempt to extract a number:
bool numberNext(istream& is = cin)
{
char c = is.peek();
return '0' <= c && c <= '9';
}
It's also possible to extract C and C++ strings from an input stream:
char buffer[80];
cin.getline(buffer, 80);
string response;
getline(cin, response);
The first version extracts up to 79 characters from cin and places them in buffer, followed by a null character. For both functions the extraction stops if the end of file or a newline character is encountered. If a newline character is encountered, it is discarded. This can cause problems in VC++. For example, the getline in the echo function:
void echo()
{
string line;
cout << "Enter a line: ";
getline(cin, line);
cout << "Line = " << line << '\n';
}
discards the newline character before it terminates the input. This means the user must press the Return or Enter key twice to see the echo.
Here's a home-grown version that works:
istream& getLine(istream& in, string& str)
{
str = "";
char c;
do
{
c = in.get();
if (c != '\n') str += c;
}
while (in && c != '\n');
return in;
}
Translators are available for input streams, too. For example:
double x;
cin >> x;
extracts characters up to the first white space character (tab, blank, or newline), translates these characters into the binary representation of a double, then assigns this double to x.
As with the insertion operator, several extraction operations can be chained on the same line.
double x, z;
int y;
cint >> x >> y >> z;
Right shift is right-to-left associative, so this is the same as:
(((cint >> x) >> y) >> z);
We can read strings and characters using the right shift operator:
string s1, s2;
char c;
cin >> s1 >> s2 >> c;
An important difference between the extraction operator and getline is that by default, extraction terminates when the first white space is encountered. For example, the following version of echo only echoes the first word of a multi word user input:
void echo()
{
string line;
cout << "Enter a line: ";
cin >> line;
cout << "Line = " << line << '\n';
}
Let's build a little square root calculator (you'll need #include <cmath> for this):
void calc()
{
bool more = true;
double x = 0;
char response;
while(more)
{
cout << "Enter a number, ";
cout << "then press the Enter or Return key: ";
cin >> x;
if (x < 0)
cout << "The number must be non negative\n";
else
{
cout << "sqrt(" << x << ") = " << sqrt(x) << '\n';
cout << "Press any key to continue or q to quit: ";
cin >> response;
// response = cin.get(); this fails!
more = (response != 'q') && (response != 'Q');
}
}
}
The structure of our control loop is standard. It perpetually:
1. prompts the user for a number
2. reads a number
3. prints the square root of the number
4. asks the user if he wants to continue
5. reads the response
6. sets the loop control variable
Prompting the user for input is important because extraction is a blocking operations. This means the program blocks until it receives input. Without the prompt this can create the impression that the program has crashed.
On most systems the characters typed are not actually extracted until the user presses the Enter or Return key. In a commercial application it is wise to warn the user about this in the prompt.
Notice that the response is initially read using cin.get(). This fails on most systems because characters are actually extracted from a buffer that contains the entire line that was typed. The buffer still contains the newline character that terminated the numeric input. Because cin.get() doesn't skip white space, it reads this newline character as the response. The response is different from 'q', so the loop is reentered. The replacement:
cin >> response;
works because the right shift operator skips white space, including the newline character. However, the user can't type "any" key to continue, as the prompt suggests. Pressing the space bar, tab key, Return key, or Enter key moves the cursor, but doesn't cause any character to be extracted.
To solve this problem, we can flush the input buffer after the number is read. This is done with the synchronize function:
while(more)
{
cout << "Enter a number, ";
cout << "then press the Enter or Return key: ";
cin >> x;
cin.sync();
if (x < 0)
cout << "The number must be non negative\n";
else
{
cout << "sqrt(" << x << ") = " << sqrt(x) << '\n';
cout << "Press any key to continue or q to quit: ";
response = cin.get();
more = (response != 'q') && (response != 'Q');
}
}
A much bigger problem is what happens if the user enters the string "nine" instead of the number 9? The extraction cin >> x; will fail because the string can't be converted into a number. At that point cin enters the fail state. No input operations can be performed while an input stream is in this state. On most systems this means the program spins wildly around the while loop, prompting the user, but never pausing to read an answer. (Try <Ctrl>c to interrupt.)
When a stream is in the fail state, it turns into 0, the null pointer. The state of the stream can be set back to good using the clear() function. Here's a template function that uses these features to safely read from cin:
template <class Data>
void getData(Data& var, const string& prompt)
{
Data response;
cout << prompt + " -> ";
while (!(cin >> response))
{
cerr << "Invalid entry, ";
cerr << "please try again or type <Ctrl>c to quit\n";
cin.clear(); // cin.state = good
cin.sync(); // flush buffer
cout << prompt + " -> ";
}
cout << "You entered " << response << '\n';
var = response;
}
The end of the control loop is so common, we might consider making it into a reusable function:
bool getResponse(const string& question)
{
string response;
bool yes, no, invalid;
do
{
cout << question + " (y/n) -> ";
cin >> response;
yes = response == "y" || response == "Y";
no = response == "n" || response == "N";
invalid = !(yes || no);
if (invalid)
cout << "Please type y for yes, n for no.\n";
}
while (invalid);
return yes;
}
Here's a new version of our control loop, we no longer will need the response variable:
while(more)
{
getData(x, "Enter a number");
if (x < 0)
cout << "The number must be non negative\n";
else
{
cout << "sqrt(" << x << ") = " << sqrt(x) << '\n';
more = getResponse("Continue?");
}
}
Here's a sample output produced by the program:
Enter a number -> 36
You entered 36
sqrt(36) = 6
Continue? (y/n) -> y
Enter a number -> 49
You entered 49
sqrt(49) = 7
Continue? (y/n) -> t
Please type "y" for "yes," "n" for "no".
Continue? (y/n) -> y
Enter a number -> -34
You entered -34
The number must be non negative
Enter a number -> three
Invalid entry, please try again or type <Ctrl>c to quit
Enter a number -> 3
You entered 3
sqrt(3) = 1.73205
Continue? (y/n) -> n
Input File Streams
We can construct an input file stream from a file name:
ifstream ifs("data.txt");
If the file isn't found or if its permission level doesn't allow reading, then ifs will enter the fail state and it is set equal to 0:
if (!ifs) error("can't open file");
where error is from my own utils.h:
void error(const string& gripe = "?")
{
if (DEBUG_MODE)
{
cerr << "Error: " << gripe << "!\n";
exit(1);
}
else
throw runtime_error (gripe);
}
We can use the null test again to determine when the end of the file is reached. This is more reliable than the eof() function, because that returns false even when only white space characters remain in the file:
while(ifs >> val) { ... }
For example, the following function computes the average of a file of numbers:
double avg(const string& source)
{
double num, total = 0;
int count = 0;
ifstream ifs(source.c_str()); // won't work with C++ strings!
if (!ifs)
error("can't open file");
while (ifs >> num)
{
total += num;
count++;
}
return total/int(count);
}
Of course if a non number is encountered in the file, even so much as a punctuation mark, the loop will exit and return the average of the numbers seen.
Everyone's favorite file function changes lower case letters to upper case letters. I'm using the toupper() function defined in the standard C library, so I need to include <cctype>:
void toUpper(const string& source, const string& dest)
{
char c;
ifstream ifs(source.c_str());
ofstream ofs(dest.c_str());
if (ifs)
while (ifs.get(c))
ofs.put(toupper(c));
else
error("can't open file");
}
Counting words and lines in a file is harder than you'd think. I increment my word counter each time I encounter a non letter following a letter. If the last character in the file was a letter, I increment my word counter again. I increment my line counter each time I encounter a newline character. If the last character in the file was not a newline, I increment my line count once more. Can you think of other improvements?
void fileStats(const string& source)
{
char c;
int charCount = 0, wordCount = 0, lineCount = 0;
bool wasLetter = false;
ifstream ifs(source.c_str());
if (ifs)
{
while (ifs.get(c))
{
charCount++;
if (isalpha(c))
wasLetter = true;
else
{
if (c == '\n') lineCount++;
if (wasLetter) wordCount++;
wasLetter = false;
}
}
if (wasLetter) wordCount++;
if (c != '\n') lineCount++;
cout << "char count = " << charCount << '\n';
cout << "word count = " << wordCount << '\n';
cout << "line count = " << lineCount << '\n';
}
else
error("can't open file");
}
Grep is a famous UNIX utility that searches a given file line-by-line for a given pattern. It prints lines, with their line numbers, that contain the pattern. The ultimate pattern matching algorithm is the subject of an upper division algorithms course. I simply use string::find():
void grep(const string& pattern, const string& file)
{
int lineNum = 0;
string line;
ifstream ifs(file.c_str());
if (ifs)
while (getline(ifs, line))
{
lineNum++;
if (string::npos != line.find(pattern))
cout << lineNum << ":\t" << line << '\n';
}
else
error("can't open file");
}
Notice that all of these functions depend on the fact that get(), getline(), and >> return the stream as a value after each extraction.
Input String Streams
Strings can also be character sources for input string streams. Here's the mate to the earlier template function that translated values to strings. This one translates strings into values. We must pass the value as a reference parameter so C++ can infer Value from calls to the function:
template <class Value>
void fromString(Value& val, const string& str)
{
istringstream is(str);
is >> val; // hope >> is defined for Value
}
Stream Structure
Officially, a stream consists of three parts: a translator, a specification, and a buffer. The buffer encapsulates and manages a pointer to the I/O source or sink and an array of characters waiting to be transferred to the program or to the output sink. Buffers are instances of the streambuf class. Istream and ostream instances are translators. Specifications are instances of the ios class. The specification maintains the status and format state of the stream as well as a pointer to the buffer. Oddly, the designers of the stream library picked inheritance as the relationship between translators and specifiers:
A stream inherits functions from its specification that allow programmers to determine its state. For example, if s is a stream, then:
int state = s.rdstate();
Although the state is an integer, its gets interpreted as a sequence of flags or bits indicating the status of s. We can access these bits directly with the predicates:
s.good() // next operation might succeed
s.eof() // end of file seen
s.fail() // next operation will fail
s.bad() // stream is corrupted
If s.good() is false, then subsequent extraction operations are no-ops. If s.bad() is true, forget it. The stream is broken. If s.fail() is true, then the stream can be repaired. Flush the remaining characters and set the state back to the good state using the setstate() function:
s.sync();
s.setstate(ios::goodbit);
These functions are useful when we want to implement our own translators. Recall the Rational number class declared earlier:
struct Rational
{
int num, den;
// etc.
};
When a user enters a rational number, we expect it to have the form a/b or a, where a and b are integers. Failure to meet any of these requirements results in setting the input stream state to fail, and returning immediately. For example, the inputs 3/x, 3\5, and x/5 all result is a failed state. But what happens if the user types a single integer followed by some white space and a newline? We should accept this as a rational with numerator 1. We can use peek() to see if the next character is white space. Of course if the user enters 3 /5, this will be interpreted as 3/1 and the /5 will be left in the buffer:
istream& operator>>(istream& is, Rational& rat)
{
rat.den = 1;
char c;
is >> rat.num;
if (is.good())
{
if (isspace(is.peek()))
return is;
is >> c >> rat.den;
if (c == '/' && is.good())
return is;
}
is.setstate(ios::failbit);
return is;
}
We can eat whitespace using a function like this one:
istream& eatwhile(istream& is)
{
char c;
while(is)
{
c = is.get();
if (!isspace(c))
{
is.putback(c);
break;
}
}
return is;
}
Here's a handy function that allows you to determine the number of characters in the input buffer without blocking the program. It directly access the buffer using the rdbuf() function:
int moreChars(istream& is)
{
return is.rdbuf()->in_avail();
}
Many of the low level functions are used in the following, useful debugging utility. You'll need the include directive and constant declaration:
#include <cctype>
const char LF = char(10); // line feed
void showStatus(istream& is)
{
cout << "\nstream status: \n";
cout << "fail() = " << is.fail() << '\n';
cout << "good() = " << is.good() << '\n';
cout << "bad() = " << is.bad() << '\n';
cout << "eof() = " << is.eof() << '\n';
cout << "state = " << is.rdstate() << '\n';
cout << "# of chars last read = " << is.gcount() << '\n';
if (is)
{
int n = moreChars(is);
cout << "# of chars left in buffer = " << n << '\n';
if (n)
{
char next = is.peek();
if (isprint(next))
{
if (next == ' ')
cout << "peek() = SPACE\n";
else if (next == '\t')
cout << "peek() = TAB\n";
else
cout << "peek() = " << next << '\n';
}
else
{
if (next == '\r')
cout << "peek() = CR\n";
else if (next == LF)
cout << "peek() = LF\n";
else if (next == '\n')
cout << "peek = NL\n";
else
{
cout << "peek() = " << "ASCII code: "
cout << int(next) << '\n';
}
}
}
else
cout << "buffer is empty\n";
}
else
cout << "stream is null\n";
cout << '\n';
}
Format State
Output format is controlled by a sequence of flags in the specifier. Users can create their own sequence of format flags by combining predefined masks using bitwise disjunction:
const ios::fmtflags my_flags =
ios::boolalpha | // symbolic rep of true & false
ios::hex | // vs oct or dec
ios::right | // alignment
ios::fixed | // vs scientific
ios::showpos | // +3 vs 3
ios::unitbuf | // flush buffer after each output
ios::uppercase; // E vs e
The old flags are saved and the new ones installed by calling the flags function:
ios::fmtflags old_flags = cout.flags(my_flags);
In addition, the minimum width of the print field and the fill character can be set:
cout.width(20);
cout.fill('.');
Now the statement:
cout << "hi\n";
produces the output:
.................hi
The width reverts to 0 after each output operation.
Flags can be set or cleared individually using setf() and unsetf():
cout.unsetf(ios::uppercase);
cout.setf(ios::showbase);
Some flags have three values instead of two.
cout.setf(ios::oct, ios::basefield);
Programmers can control the format and precision of floating point output. For example, the statements:
long double pi = acos(-1);
long double big = 1e50;
long double small = 1e-50;
// general
cout << "pi = " << pi << '\n';
cout << "big = " << big << '\n';
cout << "small = " << small << '\n';
cout.setf(ios::scientific, ios::floatfield);
cout << "pi = " << pi << '\n';
cout << "big = " << big << '\n';
cout << "small = " << small << '\n';
cout.setf(ios::fixed, ios::floatfield);
cout << "pi = " << pi << '\n';
cout << "big = " << big << '\n';
cout << "small = " << small << '\n';
// back to general
cout.setf(0, ios::floatfield);
cout << "pi = " << pi << '\n';
cout << "big = " << big << '\n';
cout << "small = " << small << '\n';
cout.precision(4);
cout << "pi = " << pi << '\n';
cout << "big = " << big << '\n';
cout << "small = " << small << '\n';
Produces the output:
pi = 3.14159
big = 1e+050
small = 1e-050
pi = 3.141593e+000
big = 1.000000e+050
small = 1.000000e-050
pi = 3.141593
big = 100000000000000010000000000000000000000000000000000.000000
small = 0.000000
pi = 3.14159
big = 1e+050
small = 1e-050
pi = 3.142
big = 1e+050
small = 1e-050
Manipulators
Manipulators are functions that can be applied to output streams using the left shift operator syntax.
cin >> noskipws >> x;
cout << x << endl;
cout << boolalpha << true << endl;
//cout << setprecision(4) << 123.456789 << endl;
cout << hex << 42 << endl;
Input/Output Streams