(See libcp.htm for information about the header files and namespaces used below.)
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
#include <iomanip> // manipulators
using namespace std;
Input sources and output sinks— i.e., I/O 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 source. An output stream is a sequence of characters heading for an output sink. 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 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 and pipes can re-associate cout with a file or the standard input stream of another program, while cerr and clog (which only differ in their buffering strategies) cannot be redirected.
The most basic service provided by instances of the ostream class inserts a single character into an output stream:
cout.put('b');
cout.put('a');
cout.put('t');
cout.put('\n');
We can insert a whole string using the write() function:
cerr.write("bat\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 42 to "42", then
inserts
cout << 3.1416; // translates 3.1416 to "3.1416", 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.
Of course we can also use translators to write ordinary strings and characters:
cout <<
"bat";
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 integers (the numerator and denominator) encapsulated in a struct:
struct Rational
{
int num, den; // = 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.
Writing to a file is easy. 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 from the ostream class, hence all of the operations discussed above, including left shift, can be used with file streams:
For example, the following code simply writes some strings and integers to a file called "data":
ofstream fs("data");
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';
Of course the file created is simply a text file that can be inspected using an ordinary editor.
Unfortunately, streams predated C++ strings; consequently, a large body of translators (i.e., insertion operators such as the one defined earlier for rationals) were developed that converted instances of programmer-defined types into strings that were immediately inserted into output streams. It would seem that these translators couldn't be used in situations where a value needed to be converted into a string that could be used for something other than output.
To remedy this situation, C++ allows strings to be output sinks. An output stream associated with a string is called an output string stream (ostringstream). Like output file streams, the class of output string streams is derived from ostream, hence inherits all of the functionality associated with that class:
To convert a value to a string, we insert the value into an output string stream using the insertion operator inherited from output streams. The str() member function can then be used to extract the string created inside the string stream. The following function formalizes this idea and makes a valuable addition to our utility library:
template <typename Value>
string toString(const Value& val)
{
ostringstream os;
os << val; // I hope ostream
<< Value is defined!
return os.str();
}
Here is a sample segment of code that uses this template:
string s1 = "the answer";
string s2 = toString(6 * 7);
string s3 = s1 + " = " + s2;
cout << s3 << endl; // prints "the answer = 42"
Output is easy, but input is filled with pitfalls that await the unwary due to the gap between the actual and the expected types of incoming data.
Input streams are instances of the istream class. Analogous to output streams, the input file stream class (ifstream) and the input string stream class (istringstream) are derived from the istream class:
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() doesn't make sense:
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 without extracting it:
c = 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:
inline bool numberIsNext(istream& is = cin)
{
return isdigit(is.peek()); // isdigit()
from <cctype>
}
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.[1]
Translators are available for input streams, too. For example:
double x;
cin >> x;
extracts characters up to the first character that cannot be construed as part of a double, translates these characters into the binary representation of a double, then assigns this double to x.
Of course if the extracted characters can't be construed as a double, for example, if the user types "nine" instead of "9", then the translator quietly fails. No error message is printed, no exception is thrown. Instead, a failure flag deep inside cin is set and subsequent read operations do nothing when executed.
A quick way to determine if the failure flag for a stream has been set is to compare the stream to 0, the null pointer. Since the extraction operator always returns its stream argument, we can extract characters and test for failure in a single operation:
while(!(stream >> x)) repair(stream);
process(x);
As with the insertion operator, several extraction operations can be chained on the same line.
double x, z;
int y;
cin >> x >> y >> z;
Right shift is right-to-left associative, so this is the same as:
(((cin >> x) >> y) >> z);
Of course 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 the get() and getline() is that by default, extraction terminates when the first white space is encountered. This can lead to obscure bugs. For example, assume a simple calculator perpetually prompts the user for a number, calculates and displays the square root of the number, then asks the user if he wants to quit:
void calc()
{
bool more = true;
double x = 0;
char response;
while(more)
{
cout << "Enter a non
negative number: ";
cin >> x;
if (x < 0)
cout << "The number
must be non negative\n";
else
{
cout << "square root =
" << sqrt(x) << endl;
cout << "Press any key
to continue or q to quit: ";
// response = cin.get(); this
fails!
cin >> response;
more = (response != 'q');
}
}
}
Notice that response was 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. If the user entered the string "49\n" in response to the first prompt, then after "49" was extracted and converted to 49, 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 to the second prompt. 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 second prompt suggests. Pressing the space bar, tab key, Return key, or Enter key— the usual "any key" candidates —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 non
negative number: ";
cin >> x;
cin.sync();
// flush cin's buffer
if (x < 0)
cout << "The number must
be non negative\n";
else
{
cout << "square root =
" << sqrt(x) << endl;
cout << "Press any key to
continue or q to quit: ";
response = cin.get();
more = (response != 'q');
}
}
A much bigger problem is what happens if the user enters the string "forty-nine" instead of the string "49"? The extraction of 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.)
The failure flag of a stream can be cleared using the clear() function. Here's a template function that uses these features to safely read from cin:
template <typename 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(); // clear failure flag
cin.sync(); // flush buffer
cout << prompt + " ->
";
}
cout << "You entered "
<< response << endl;
var = response;
}
In our calculator example we can now replace the lines:
cout << "Enter a number: ";
cin >> x;
by the single line:
getData(x, "Enter a number");
Alternatively (better):
while(more)
{
cout << "Enter a non
negative number: ";
cin >> x;
if (!cin)
{
cout << "Error: wrong
format\n";
cin.clear();
cin.sync();
continue;
}
cin.sync();
// flush cin's buffer
if (x < 0)
cout << "The number must
be non negative\n";
else
{
cout << "square root =
" << sqrt(x) << endl;
cout << "Press any key to
continue or q to quit: ";
response = cin.get();
more = (response != 'q');
}
}
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 (i.e., it is set equal to 0):
if (!ifs)
{
cerr << "can't open
file\n";
exit(1);
}
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/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.
Strings are 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 <typename Value>
void fromString(Value& val, const string& str)
{
istringstream is(str);
is >> val; // I hope istream
>> Value is defined!
}
For example, here's how we could convert the string "123" into the integer 123:
int x;
fromString(x, "123"); // x = 123
Officially, a stream consists of three parts: a translator, a specification, and a stream buffer. The stream buffer encapsulates and manages an array of characters (i.e. the character buffer) waiting to be transferred to the program or to the output sink.[2] The specification maintains the status and format state of the stream as well as a pointer to the stream buffer. Stream buffers are instances of the streambuf class, istream and ostream instances are translators, and specifications are instances of the ios class. Oddly, the designers of the stream library picked specialization as the relationship between translators and specifiers:
Programmers rarely need to directly access stream buffers, but if they do, they can use the rdbuf() function inherited from the ios base class to obtain a pointer to it. For example, one drawback of the input stream peek() function is that if there are no characters in the buffer, it blocks program execution until one shows up. This makes it unusable for situations where programmers want to poll multiple input streams. However, the streambuf class has a member function called in_avail() that returns the number of characters in the buffer without blocking. We can use this to create a useful stream-level version of the function:
inline int moreChars(istream& is = cin)
{
return is.rdbuf()->in_avail();
}
A stream also inherits functions from its specification that allow programmers to determine its state. For example, if s is an input stream, then:
int state = s.rdstate();
Although the state is an integer, it 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 should all result in a failed state. But what happens if the user types a single integer followed by some white space and a newline? Our policy will be to interpret this as a rational with denominator 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; // slash stored here
is >> rat.num;
if (is.good()) // valid numerator read
{
if (isspace(is.peek())) return is;
// rat = num/1
is >> c >> rat.den;
if (c == '/' && is.good())
return is; // rat = num/den
}
is.setstate(ios::failbit |
is.rdstate());
return is;
}
The line:
is.setstate(ios::failbit);
explicitly sets the fail bit if is. Of course if the user enters a non-integer numerator or denominator, the fail bit will already be set. However, if the user enters a valid numerator and denominator, but doesn't separate them with a slash, for example, if the user enters 3\5 instead of 3/5, then the fail bit won't be set automatically, so we must do it manually.
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 | // hex vs. oct vs. dec
ios::right | // alignment
ios::fixed | // fixed vs. scientific vs. float
ios::showpos | // +3 vs. 3
ios::unitbuf | // flush buffer after each output
ios::uppercase; // 3E5 vs. 3e5
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, assume the following declarations are made:
long double pi = acos(-1);
long double big = 1e50;
long double small = 1e-50;
Using the default format settings, executing the lines:
cout << "pi = " << pi << '\n';
cout << "big = " << big << '\n';
cout << "small = " << small << '\n';
produces the output:
pi = 3.14159
big = 1e+050
small = 1e-050
If we change the floating point field in the format flag to scientific notation:
cout.setf(ios::scientific, ios::floatfield);
then the insertion statements will produce the output:
pi = 3.141593e+000
big = 1.000000e+050
small = 1.000000e-050
If we change the floating point field in the format flag to fixed point notation:
cout.setf(ios::fixed, ios::floatfield);
then the insertion statements will produce the output:
pi = 3.141593
big = 100000000000000010000000000000000000000000000000000.000000
small = 0.000000
We can restore the general format by setting the floating point field to 0:
cout.setf(0, ios::floatfield);
We can change the precision of the output using the precision() member function:
cout.precision(4);
Notice that when we execute the insertion commands, only three digits are shown beyond the decimal point in pi:
pi = 3.142
big = 1e+050
small = 1e-050
Manipulators are functions that can be applied to output streams using the left shift operator syntax. For example, executing the statements:
cout << boolalpha << true << endl;
cout << setprecision(4) << 123.456789 << endl;
cout << hex << 42 << endl;
produces the output:
true
123.5
2a
Consult your online documentation or [STR] for a complete list of manipulators and to learn how to create your own manipulators.
C++ also provides an iostream class that multiply inherits from istream and ostream. The main purpose of iostream is to serve as a base class for fstream— the class of all file streams that can be opened for reading or writing —and stringstream— the class of all string streams that can be read from or written to:
For example, we initially declare an fstream without a file name:
fstream fs;
The open() function is used to associate the fstream with a file and a flag (statically defined in ios) indicating if the file is to opened for input or output:
fs.open("data.txt", ios::out);
if (!fs)
{
cerr << "Can't open
data.txt\n";
exit(1);
}
We can write data into a file stream the same way we would write data into and output stream:
for(int i = 0; i < 20; i++)
fs << i * i << endl; //
<< consecutive squares
If we now wish to reopen the same file for input, we first clear its error flag using the clear() function (this flag would have been set if we reached the end of the file), then close it using the close() function:
fs.clear();
fs.close();
then call the open() function a second time, but using the input flag, ios::in:
fs.open("data.txt", ios::in);
if (!fs)
{
cerr << "Can't open
data.txt\n";
exit(1);
}
We can now read data from the file the same way we would read from any input stream:
int x;
while(fs >> x)
cout << "x = " <<
x << endl;
Readers should consult [STR] or on line documentation to gain a more comprehensive view of C++ I/O.
[1] The implementations of the getline() functions in version 5.0 of Visual C++ seem to discard the newline character before it signals that data should be transferred from the buffer to the stream, thus requiring the user to hit the return key twice. Fortunately, it's easy to implement our own versions of getline() that work properly.
[2] It's too inefficient to transfer data between main memory and an I/O device one character at a time, so blocks of characters are transferred instead, even if the program only reads or writes a single character. The block of transferred characters are stored in the stream's buffer.