The Iostream Library |
4 |
![]() |
This chapter consists of an introduction to the iostream library and examples showing its use. It does not provide a complete description of the iostream library. See the iostream library man pages for more details.
Basic Structure of Iostream Interaction
By including the iostream library, a program can use any number of input or output streams. Each stream has some source or sink, which may be one of the following:
The ifstream , ofstream, and fstream classes, which are derived from istream, ostream, and iostream respectively, handle input and output with files.
The istrstream , ostrstream, and strstream classes, which are derived from istream, ostream, and iostream respectively, handle input and output to and from arrays of characters.
When you open an input or output stream, you create an object of one of these types, and associate the streambuf member of the stream with a device or file. You generally do this association through the stream constructor, so you don't work with the streambuf directly. The iostream library predefines stream objects for the standard input, standard output, and error output, so you don't have to create your own objects for those streams.
You use operators or iostream member functions to insert data into a stream (output) or extract data from a stream (input), and to control the format of data that you insert or extract.
When you want to insert and extract a new data type--one of your classes--you generally overload the insertion and extraction operators.
You usually don't need all these header files in your program. Include only the ones that contain the declarations you need. The iostreams library is part of libC, and is linked automatically by the CC driver.
Output Using Iostreams
Output using iostreams usually relies on the overloaded left-shift operator (<<) which, in the context of iostream, is called the insertion operator. To output a value to standard output, you insert the value in the predefined output stream cout. For example, given a value someValue, you send it to standard output with a statement like:
cout << someValue; |
The insertion operator is overloaded for all built-in types, and the value represented by someValue is converted to its proper output representation. If, for example, someValue is a float value, the << operator converts the value to the proper sequence of digits with a decimal point. Where it inserts float values on the output stream, << is called the float inserter. In general, given a type X, << is called the X inserter. The format of output and how you can control it is discussed in the ios(3C++) man page.
cout << someValue << anotherValue; |
cout << someValue << " " << anotherValue; |
cout << a+b; // + has higher precedence than << cout << (a+b); cout << (a&y); // << has precedence higher than & cout << a&y; // probably an error: (cout << a) & y |
Defining Your Own Insertion Operator
Code Example 4-1 defines a string class:
The insertion and extraction operators must in this case be defined as friends because the data part of the string class is private.
ostream& operator<< (ostream& ostr, const string& output) { return ostr << output.data; } |
cout << string1 << string2; |
Handling Output Errors
Generally, you don't have to check for errors when you overload operator<< because the iostream library is arranged to propagate errors.
if (!cout) error( "output error"); |
if (cout << x) return ; // return if successful |
You can also use the function good, a member of ios:
if ( cout.good() ) return ; // return if successful |
The error bits are declared in the enum:
enum io_state { goodbit=0, eofbit=1, failbit=2, badbit=4, hardfail=0x80} ; |
For details on the error functions, see the iostream man pages.
Flushing
As with most I/O libraries, iostream often accumulates output and sends it on in larger and generally more efficient chunks. If you want to flush the buffer, you simply insert the special value flush. For example:
cout << "This needs to get out immediately." << flush ; |
flush is an example of a kind of object known as a manipulator, which is a value that can be inserted into an iostream to have some effect other than causing output of its value. It is really a function that takes an ostream& or istream& argument and returns its argument after performing some actions on it (see "Manipulators" on page 67).
Binary Output
To obtain output in the raw binary form of a value, use the member function write as shown in the following example. This example shows the output in the raw binary form of x.
cout.write((char*)&x, sizeof(x)); |
The previous example violates type discipline by converting &x to char*. Doing so is normally harmless, but if the type of x is a class with pointers, virtual member functions, or one that requires nontrivial constructor actions, the value written by the above example cannot be read back in properly.
Input Using Iostreams
Input using iostream is similar to output. You use the extraction operator >> and you can string together extractions the way you can with insertions. For example:
cin >> a >> b ; |
This statement gets two values from standard input. As with other overloaded operators, the extractors used depend on the types of a and b (and two different extractors are used if a and b have different types). The format of input and how you can control it is discussed in some detail in the ios(3C++) man page. In general, leading whitespace characters (spaces, newlines, tabs, form-feeds, and so on) are ignored.
Defining Your Own Extraction Operators
When you want input for a new type, you overload the extraction operator for it, just as you overload the insertion operator for output.
Code Example 4-2 string Extraction Operator |
istream& operator>> (istream& istr, string& input)
{
const int maxline = 256;
char holder[maxline];
istr.get(holder, maxline, `\n');
input = holder;
return istr;
}
The get function reads characters from the input stream istr and stores them in holder until maxline-1 characters have been read, or a new line is encountered, or EOF, whichever happens first. The data 1n holder is then null-terminated. Finally, the characters in holder are copied into the target string.
Using the char* Extractor
This predefined extractor is mentioned here because it can cause problems. Use it like this:
char x[50]; cin >> x; |
This extractor skips leading whitespace and extracts characters and copies them to x until it reaches another whitespace character. It then completes the string with a terminating null (0) character. Be careful, because input can overflow the given array.
char * p; // not initialized cin >> p; |
There is no telling where the input data will be stored, and it may cause your program to abort.
Reading Any Single Character
In addition to using the char extractor, you can get a single character with either form of the get member function. For example:
char c; cin.get(c); // leaves c unchanged if input fails int b; b = cin.get(); // sets b to EOF if input fails |
Here is a way to skip only blanks, stopping on a tab, newline, or any other character:
Note - Unlike the other extractors, the char extractor does not skip leading whitespace.
int a; do { a = cin.get(); } while( a == ' ' ); |
Binary Input
If you need to read binary values (such as those written with the member function write), you can use the read member function. The following example shows how to input the raw binary form of x using the read member function, and is the inverse of the earlier example that uses write.
cin.read((char*)&x, sizeof(x)); |
Peeking at Input
You can use the peek member function to look at the next character in the stream without extracting it. For example:
if (cin.peek() != c) return 0; |
Extracting Whitespace
By default, the iostream extractors skip leading whitespace. You can turn off the skip flag to prevent this from happening. The following example turns off whitespace skipping from cin, then turns it back on:
cin.unsetf(ios::skipws); // turn off whitespace skipping . . . cin.setf(ios::skipws); // turn it on again |
istr >> ws; |
Handling Input Errors
By convention, an extractor whose first argument has a nonzero error state should not extract anything from the input stream and should not clear any error bits. An extractor that fails should set at least one error bit.
Code Example 4-3 Handling Extraction Errors |
#include <unistd.h>
#include <iostream.h>
void error (const char* message) {
cerr << message << "\n" ;
exit(1);
}
main() {
cout << "Enter some characters: ";
int bad;
cin >> bad;
if (!cin) error("aborted due to input error");
cout << "If you see this, not an error." << "\n";
return 0;
}
Class ios has member functions that you can use for error handling. See the man pages for details.
Using Iostreams with stdio
You can use stdio with C++ programs, but problems can occur when you mix iostreams and stdio in the same standard stream within a program. For example, if you write to both stdout and cout, independent buffering occurs and produces unexpected results. The problem is worse if you input from both stdin and cin, since independent buffering may turn the input into trash.
ios::sync_with_stdio(); |
Such a connection is not the default because there is a significant performance penalty when the predefined streams are made unbuffered as part of the connection. You can use both stdio and iostreams in the same program applied to different files . That is, you can write to stdout using stdio routines and write to other files attached to iostreams. You can open stdio FILEs for input and also read from cin so long as you don't also try to read from stdin.
Creating Iostreams
To read or write a stream other than the predefined iostreams, you need to create your own iostream. In general, that means creating objects of types defined in the iostream library. This section discusses the various types available. Dealing with Files Using Class fstream
Dealing with files is similar to dealing with standard input and standard output; classes ifstream, ofstream, and fstream are derived from classes istream, ostream, and iostream, respectively. As derived classes, they inherit the insertion and extraction operations (along with the other member functions) and also have members and constructors for use with files.
Code Example 4-4 Copying Files with Streams |
ifstream fromFile("thisFile");
if (!fromFile)
error("unable to open 'thisFile' for input");
ofstream toFile ("thatFile");
if ( !toFile )
error("unable to open 'thatFile' for output");
char c ;
while (toFile && fromFile.get(c)) toFile.put(c);
Note - It is, of course, undesirable to copy a file this way, one character at a time. This code is provided just as an example of using fstreams. You should instead insert the streambuf associated with the input stream into the output stream. See "Streambufs" on page 73, and the man page sbufpub(3C++).
enum open_mode {binary=0, in=1, out=2, ate=4, app=8, trunc=0x10, nocreate=0x20, noreplace=0x40}; |
You can open a file for both input and output. For example, the following code opens file someName for both input and output, attaching it to the fstream variable inoutFile.
Note - The binary flag is not needed on Unix, but is provided for compatibility with systems which do need it. Portable code should use the binary flag when opening binary files.
fstream inoutFile("someName", ios::in|ios::out); |
Declaring an fstream Without Specifying a File
You can declare an fstream without specifying a file and open the file later. For example, the following creates the ofstream toFile for writing.
ofstream toFile; toFile.open(argv[1], ios::out); |
Opening and Closing Files
You can close the fstream and then open it with another file. For example, to process a list of files provided on the command line:
ifstream infile; for (char** f = &argv[1]; *f; ++f) { infile.open(*f, ios::in); ...; infile.close(); } |
Opening a File Using a File Descriptor
If you know a file descriptor, such as the integer 1 for standard output, you can open it like this:
ofstream outfile; outfile.attach(1); |
When you open a file by providing its name to one of the fstream constructors or by using the open function, the file is automatically closed when the fstream is destroyed (by a delete or when it goes out of scope). When you attach a file to an fstream, it is not automatically closed.
Repositioning within a File
You can alter the reading and writing position in a file. Several tools are supplied for this purpose.
enum seek_dir { beg=0, cur=1, end=2 } |
streampos original = aFile.tellp(); //save current position aFile.seekp(0, ios::end); //reposition to end of file aFile << x; //write a value to file aFile.seekp(original); //return to original position |
aFile.seekp(-10, ios::end); |
moves to 10 bytes from the end while
aFile.seekp(10, ios::cur); |
moves to 10 bytes forward from the current position.
Note - Arbitrary seeking on text streams is not portable, but you can always return to a previously saved streampos value.
Assignment of Iostreams
Iostreams does not allow assignment of one stream to another.
Format Control
Format control is discussed in detail in the in the manual page ios(3C++).
Manipulators
Manipulators are values that you can insert into or extract from iostreams to have special effects.
|
Predefined Manipulator |
Description |
1 |
ostr << dec, istr >> dec |
Makes the integer conversion base 10. |
2 |
ostr << endl |
Inserts a newline character ('\n') and invokes ostream::flush(). |
3 |
ostr << ends |
Inserts a null (0) character. Useful when dealing with strstreams. |
4 |
ostr << flush |
Invokes ostream::flush(). |
5 |
ostr << hex, istr >> hex |
Makes the integer conversion base 16. |
6 |
ostr << oct, istr >> oct |
Make the integer conversion base 8. |
7 |
istr >> ws |
Extracts whitespace characters (skips whitespace) until a non-whitespace character is found (which is left in istr). |
8 |
ostr << setbase(n), istr >> setbase(n) |
Sets the conversion base to n (0, 8, 10, 16 only). |
9 |
ostr << setw(n), istr >> setw(n) |
Invokes ios::width(n). Sets the field width to n. |
10 |
ostr << resetiosflags(i), istr >> resetiosflags(i) |
Clears the flags bitvector according to the bits set in i. |
11 |
ostr << setiosflags(i), istr >> setiosflags(i) |
Sets the flags bitvector according to the bits set in i. |
12 |
ostr << setfill(c), istr >> setfill(c) |
Sets the fill character (for padding a field) to c. |
13 |
ostr << setprecision(n), istr >> setprecision(n) |
Sets the floating-point precision to n digits. |
You can define your own manipulators. There are two basic types of manipulator:
An example of a tab manipulator that inserts a tab in an ostream is:
ostream& tab(ostream& os) { return os << '\t' ; } ... cout << x << tab << y ; |
This is an elaborate way to achieve the following:
const char tab = '\t'; ... cout << x << tab << y; |
Here is another example, which cannot be accomplished with a simple constant. Suppose we want to turn whitespace skipping on and off for an input stream. We can use separate calls to ios::setf and ios::unsetf to turn the skipws flag on and off, or we could define two manipulators, as shown in Code Example 4-5:
Code Example 4-5 Toggle Whitespace Skipping |
#include <iostream.h>
#include <iomanip.h>
istream& skipon(istream &is) {
is.setf(ios::skipws, ios::skipws);
return is;
}
istream& skipoff(istream& is) {
is.unsetf(ios::skipws);
return is;
}
...
int main ()
{
int x,y;
cin >> skipon >> x >> skipoff >> y;
return 1;
}
Parameterized Manipulators
One of the parameterized manipulators that is included in iomanip.h is setfill. setfill sets the character that is used to fill out field widths. It is implemented as shown in Code Example 4-6:
Code Example 4-6 Parameterized Manipulators |
//file setfill.cc
#include<iostream.h>
#include<iomanip.h>
//the private manipulator
static ios& sfill(ios& i, int f) {
i.fill(f);
return i;
}
//the public applicator
smanip_int setfill(int f) {
return smanip_int(sfill, f);
}
A parameterized manipulator is implemented in two parts:
Code Example 4-7 Manipulator print_hex |
#include <iostream.h>
#include <iomanip.h>
static ostream& xfield(ostream& os, long v) {
long save = os.setf(ios::hex, ios::basefield);
os << v;
os.setf(save, ios::basefield);
return os;
}
omanip_long print_hex(long v) {
return omanip_long(xfield, v);
}
Strstreams: Iostreams for Arrays
See the strstream(3C++) man page.
Stdiobufs: Iostreams for stdio files
See the stdiobuf(3C++) man page.
Streambufs
Iostreams are the formatting part of a two-part (input or output) system. The other part of the system is made up of streambufs, which deal in input or output of unformatted streams of characters. Working with Streambufs
A streambuf consists of a stream or sequence of characters and one or two pointers into that sequence. Each pointer points between two characters. (Pointers cannot actually point between characters, but it is helpful to think of them that way.) There are two kinds of streambuf pointers:
Apart from creating your own special kind of streambuf, you may want to access the streambuf associated with an iostream to access the public member functions, as described in the man pages referenced above. In addition, each iostream has a defined inserter and extractor which takes a streambuf pointer. When a streambuf is inserted or extracted, the entire stream is copied.
Here is another way to do the file copy discussed earlier, with the error checking omitted for clarity:
ifstream fromFile("thisFile"); ofstream toFile ("thatFile"); toFile << fromFile.rdbuf(); |
fromFile >> toFile.rdbuf(); |
The source file is then extracted into the destination. The two methods are entirely equivalent.
Iostream ManPages
A number of C++ man pages give details of the iostream library. Table 4-3 gives an overview of what is in each man page.
Man Page |
Overview |
ios.intro |
Gives an introduction to and overview of iostreams. |
filebuf |
Details the public interface for the class filebuf, which is derived from streambuf and is specialized for use with files. See the sbufpub(3C++) and sbufprot(3C++) man pages for details of features inherited from class streambuf. Use the filebuf class through class fstream. |
fstream |
Details specialized member functions of classes ifstream, ofstream, and fstream, which are specialized versions of istream, ostream, and iostream for use with files. |
ios |
Details parts of class ios, which functions as a base class for iostreams. It contains state data common to all streams. |
istream |
Details the following:
|
manip |
Describes the input and output manipulators defined in the iostream library. |
ostream |
Details the following:
|
sbufprot |
Describes the interface needed by programmers who are coding a class derived from class streambuf. Also refer to the sbufpub man page because some public functions are not discussed in the sbufprot man page. |
sbufpub |
Details the public interface of class streambuf, in particular, the public member functions of streambuf. This man page contains the information needed to manipulate a streambuf-type object directly, or to find out about functions that classes derived from streambuf inherit from it. If you want to derive a class from streambuf, also see the sbufprot man page. |
stdiobuf |
Contains a minimal description of class stdiobuf, which is derived from streambuf and specialized for dealing with stdio FILEs. See the sbufpub(3C++) man page for details of features inherited from class streambuf. |
strstream |
Details the specialized member functions of strstreams, which are implemented by a set of classes derived from the iostream classes and specialized for dealing with arrays of characters. |
ssbuf |
Details the specialized public interface of class strstreambuf, which is derived from streambuf and specialized for dealing with arrays of characters. See the sbufpub(3C++) man page for details of features inherited from class streambuf. |
Iostream Terminology
The iostream library descriptions often use terms similar to terms from general programming, but with specialized meanings. Table 4-4 defines these terms as they are used in discussing the iostream library.
Iostream Term |
Definition |
Buffer |
A word with two meanings, one specific to the iostream package and one more generally applied to input and output. When referring specifically to the iostream library, a buffer is an object of the type defined by the class streambuf. A buffer, generally, is a block of memory used to make efficient transfer of characters for input of output. With buffered I/O, the actual transfer of characters is delayed until the buffer is full or forceably flushed. An unbuffered buffer refers to a streambuf where there is no buffer in the general sense defined above. This chapter avoids use of the term buffer to refer to streambufs. However, the man pages and other C++ documentation do use the term buffer to mean streambufs. |
Extraction |
The process of taking input from an iostream. |
Fstream |
An input or output stream specialized for use with files. Refers specifically to a class derived from class iostream when printed in courier font. |
Insertion |
The process of sending output into an iostream. |
Iostream |
Generally, an input or output stream. |
Iostream library |
The library implemented by the include files iostream.h, fstream.h, strstream.h, iomanip.h, and stdiostream.h. Because iostream is an object-oriented library, you should extend it. So, some of what you can do with the iostream library is not implemented. |
Stream |
An iostream, fstream, strstream, or user-defined stream in general. |
Streambuf |
A buffer that contains a sequence of characters with a put or get pointer, or both. When printed in courier font, it means the particular class. Otherwise, it refers generally to any object of class streambuf or a class derived from streambuf. Any stream object contains an object, or a pointer to an object, of a type derived from streambuf. |
Strstream |
An iostream specialized for use with character arrays. It refers to the specific class when printed in courier font. |