The C++ I/O System
- I/O Class Hierarchy (simplified)
ios_base | ios / \ istream ostream | \ / | | iostream | | | | ifstream fstream ofstream- The class ios_base -- public variables and methods
- The derived classes istream, ostream
, ifstream, ofstream- Overloads of operators << and >>
- Manipulators
The C++ I/O system defined in the IOStreams library is quite complex, extraordinarily useful and flexible, and quite beautiful in design. The current system resides in the namespace std, but is the result of at least a decade of evolution fuelled by theory, experiment, and the ISO standardization process.
A comprehensive treatment of C++ IOStreams is well beyond the scope of these notes. In fact there is a very good 640 page treatise on the subject that is recommended for further study (see [Standard C++ IOStreams and Locales - Advanced Programmers Guide and Reference, by Angelika Langer and Klaus Kreft, Addison Wesley, 2000]). What is intended here is a detailed introduction to the most useful instantiations in this library, sufficient for most programming course work. For professional levels of expertise, you are strongly encouraged to obtain the Langer & Kreft reference and keep it alongside your Stroustrupp.
A portion of the iostreams class hierarchy is shown in the slide. This is the portion that we will discuss in some detail. A more complete hierarchy is as follows:
ios_base | basic_ios<charT, traits> / \ basic_istream<charT, traits> basic_ostream<charT, traits> | \ / | | basic_iostream<charT, traits> | | | | basic_ifstream<charT, traits> | basic_ofstream<charT, traits> | basic_fstream<charT, traits>
Notice that all of the classes except the base class ios_base are templates. The two template parameters represent the character type being used along with properties of that type. There are two predefined character types, the familier 1-byte ascii characters char and the 4-byte wide characters wchar_t. The type char supports ASCII, EBCDIC, and ISO 8859-2 character sets. The type wchar_t supports Unicode and ISO 10646. The user can invent other character classes and associated traits and instantiate streams using that type.
Even the diagram above does not illustrate the full generality of the IOStream library. There is support for internationalization in the form of Locales, and there is a hierarchy of buffer types that are used by iostreams. These are interesting and important features that are beyond the scope of these notes.
There is also support for exception handling built into the class hierarchy shown above, but we will not say much about that aspect either. Because we will assume the standard ASCII character set, there is no need to go into detail on the traits classes in these notes. Finally, there is a parallel set of classes for I/O into strings instead of streams. To summarize, here is a list of advanced features of the IOStream library that we will not discuss in this chapter:
For complete details on all these features of C++ IOStreams, see the Langer and Kreft reference.
For most purposes, it suffices to use the ASCII character set and stream I/O, and there are type definitions in the standard library associated with those assumptions:
typedef basic_ios < char, char_traits < char > > ios; typedef basic_istream < char, char_traits < char > > istream; typedef basic_ostream < char, char_traits < char > > ostream; typedef basic_iostream < char, char_traits < char > > iostream; typedef basic_ifstream < char, char_traits < char > > ifstream; typedef basic_ofstream < char, char_traits < char > > ofstream; typedef basic_fstream < char, char_traits < char > > fstream;
Note that making these substitutions yields the hierarchy illustrated in the slide.
In the remainder of this chapter we will discuss details of the classes
ios_base (equivalently class ios), the derived classes
istream, ostream, ifstream, ofstream, and
the collection of IO manipulators defined with these classes.
Class ios_base
namespace std { class ios_base { public: // ios_base status methods // these read and/or manipulate the status bits bool fail () const; // true iff bad or fail state bool eof () const; // true iff stream is at end of file ... // ios_base binding methods ostream* tie (ostream*); // ties current stream to specified ostream ... // ios_base formatting methods format_flags setf (format_flags flags); ... // ios_base data methods int width(int val); char fill (char fillch); ... // ios_base enumerated bitmask types enum iostate { ... }; // 4 io states, representing good, eof, bad, fail enum formatflags { ... }; // 15 format flags, such as left, showpoint, hex enum openmode { ... }; // 6 file open modes, such as in, out, binary enum seekdir { ... }; // 3 file seek modes: beg, cur, end protected: unsigned long state; // stores status bits unsigned long flags; // stores flag bits unsigned long mode; // stores mode bits int widthvalue, // initialized to 0 precisionvalue; // initialized to 0 char fillcharacter; // initialized to ' ' streambuff* streambuffer; // pointer to a streambuff object // plus other data, such as specifying tied streams } ; // end pseudo-definition of class ios_base } // namespaced std
I/O streams can be understood by exploring in detail the the class ios_base, which contains all of the public member variables and most public methods in the hierarchy. This slide shows the basic organization of the class. We explore the specifics in more detail in the following slides.
Items to take note of at this point are:
Understanding how streams work is greatly facilitated by following an
instantiation of the base class ios_base from which all of the stream
classes derive. A key to this understanding is the use of the enumerated types,
which we discuss individually in the next slides.
ios_base Status Methods
class ios_base { public: ... bool good () const; // true iff no error flag is set bool eof () const; // true iff eofbit is set bool fail () const; // true iff badbit or failbit are set bool bad () const; // true iff badbit is set bool operator ! () const; // same as fail() operator void* () const; // null pointer if fail(), non-null otherwise void clear (iostate newState = goodbit); // sets state to newState void setstate (iostate addState); // adds addState to existing state, // i.e., the new state is oldStreamState | addState ... enum iostate // io states { goodbit = 0x0000, // everything is OK; not really a bit, but required to be 0 eofbit = 0x0001, // stream is at end of file failbit = 0x0002, // the last I/O operation failed, otherwise OK badbit = 0x0004 // a serious error has occurred, stream unusable } ; ... protected: ... unsigned long state; // stores status bits ...
This slide shows one possible setup for the iostate enumerated type and the public member functions that use it. Some useful code techniques can be illuminated with this information. For example:
std::ifstream in1; ... in1.open(filename); while (!in1) { std::cout << "Cannot open file " << filename << " - try again: "; std::cin >> filename; in1.clear(); in1.open(filename); } ...
uses the ! operator to detect a problem opening a file. This operator is defined for std::ifstream objects because std::ifstream is a public derived class of ios.
You may also note that the interface allows client manipulation of the io states through both clear(state) and setstate(state). These differ in effect, as can be understood best by looking at the implementations:
void std::ios::clear (iostate newState) // default argument is goodbit { state = newState; }
void std::ios::setstate (iostate addState) { state = state | addState; // bitwise operation - set the designated bit }
The implementation of setstate uses bitwise operations. These are native operations of C that allow direct access to integral values at the bit level. Most current CPUs have hardware support for bitwise operations, making them extremely efficient, typically requiring one clock cycle to accomplish the entire operation. We will return to bitwise operations in a later chapter. Meantime, here is a table showing the bitwise operations and their semantics:
operation symbol type infix version accumulator version and & binary z = x & y z &= y or | binary z = x | y z |= y xor ^ binary z = x ^ y z ^= y not ~ unary z = ~y (na) left shift << binary z = y << n (na) right shift >> binary z = y >> n (na)
The line of code implementing setstate(addState) would be equivalent to this bitwise arithmetic, if we had addState = 00000010 and state = 00001000 and
state = state | addState = 00001000 | 00000010 = 00001010
ios_base Formatting Methods
class ios_base { public: ... fmtflags flags () const; // returns current flags (interpret using enumerated type) fmtflags flags (fmtflags newflags); // sets flags to newflags; returns oldflags fmtflags setf (fmtflags setbits); // sets those flags specified in setbits; returns oldflags // called by iomanipulator setiosflags fmtflags setf (fmtflags setbits, fmtflags mask); // unsets flags in mask, then sets flags in setbits & mask // called by iomanipulator resetiosflags fmtflags unsetf (fmtflags unsetbits); // clears flags specified in unsetbits ... enum fmtflags // parameter for ios methods setf(), unsetf(), e.g., // setf(ios::right | ios::dec | ios::showpoint | ios::fixed) { skipws , left , right , internal , dec , oct , hex , showbase , showpoint , showpos , scientific , fixed , uppercase , unitbuf , boolalpha , adjustfield , basefield , floatfield }; ... protected: ... unsigned long flags; // stores flag bits ... };
Meaning of Format Flags
enum fmtflags // parameter for ios_base methods setf(), unsetf(), e.g., // setf(ios_base::right | ios_base::fixed) { boolalpha = 0x0001, // reads and writes bool values in alphabetic left = 0x0002, // left-justify output right = 0x0004, // right-justify output internal = 0x0008, // (numeric output) prefix left..fill..number right skipws = 0x0010, // skip white space before extraction dec = 0x0020, // decimal hex = 0x0040, // hexadecimal oct = 0x0080, // octal // dec, oct, hex affect both input and output. On input, integral values // are interpreted in base notation and output uses appropirate mode. // Default is no bits set and means "C rules" apply: // output is decimal output and input of integral types is // interpreted as dec, oct, or hex depending leading/prefix: // (leading 1..9) => dec, (leading 0) => oct, (leading 0x) => hex showbase = 0x0100, // show base indicator on output (0 and 0x) showpoint = 0x0200, // show decimal point (for fixed point output) showpos = 0x0400, // out: force show of sign for positive numbers fixed = 0x0800, // out: force decimal notation for float scientific = 0x1000, // out: force scientific notation for float unitbuf = 0x2000, // out: flush buffer after each insertion uppercase = 0x4000, // out: use upper case indicators for hex and dec // convenience definitions: adjustfield = left | right | internal; basefield = dec | hex | oct; floatfield = scientific | fixed; };
ios_base Data Methods
class ios_base { public: ... char fill (char fillch); // sets fill character (default = ' '); returns old fill_character; // called by iomanipulator setfill (char) int precision () const; // returns precision_value (>= 0) int precision (int val); // sets precision (default = 0, which means as precise as possible); // called by iomanipulator setprecision (int). int width () const; // returns width value (>= 0) int width (int val); // sets width_value; note: width reset to 0 after each extraction // called by iomanipulator setw (int) ... protected: ... int width_value, precision_value; // initialized to 0 char fill_character; // initialized to ' ' ... };
Meaning of Width and Precision
// meaning of width (n) // n <= 0: no effect // n > 0: on output, sets minimum number of characters output // (filled with fill char) // on input, sets length of buffer for string extractions // Note: reset to 0 after each insertion and extraction // Called by manipulator setw (n) // meaning of precision (n) // n <= 0: default // n > 0: if ios::fixed bit is set, determines number of places // displayed after decimal point (forced if ios::showpoint // is also set) // if ios::fixed is not set, determines number of significant // digits displayed // Called by manipulator setprecision (n)
ios_base Binding Methods
class ios_base { public: ... streambuf* rdbuf (); // returns ptr to stream's streambuff object ostream* tie (); // returns ptr to the tied ostream ostream* tie (ostream*); // ties current stream to specified ostream, which is called the "tied // stream". Returns pointer to previously tied stream. // For an istream ist, this means that the tied stream will be // flushed whenever the ist needs input; for an ostream ost, this means that // the tied stream will be flushed whenever ost is flushed. // By default, cin is tied to cout. static bool sync_with_stdio (bool sync = true); // true causes cin, cerr, clog, cout to operate by means of the C standard I/O // connections stdin, stdout, and stderr; also implies unit buffering. // Returns previous value. // This is useful when mixing old C code with C++, otherwise not recommended. ... protected: ... streambuff* streambuffer; // pointer to a streambuff object ostream* tied_ostream; // pointer to an ostream object ... };
ios_base File Modes
class ios_base { public: ... enum open_mode // open modes // optional second parameter to the fstream open() method, e.g., // open(filename, ios_base::out | ios_base::binary) { in = 0x0001, // open file for input -- read out = 0x0002, // open file for output -- write ate = 0x0004, // seek to eof when file is opened // -- "automatic to end" app = 0x0008, // open file in append mode // -- writing will occur at end trunc = 0x0010, // truncate the file if it exists binary = 0x0020 // open file in binary mode // -- write bytes only, no formatting characters }; enum seek_dir // file seek modes { beg = 0x0100, // seek relative to beginning of file cur = 0x0200, // seek relative to current position end = 0x0400 // seek relative to end of file }; ... protected: ... unsigned long mode; // stores open and seek mode bits ... };
Meaning of File Modes
Open Mode Effect Effect with ...|ate in Opens text files for reading, initial position at beginning of file Initial position is at end of file out
out|truncTruncates file to empty, or creates file, for write only No effect on empty file out|app Appends; opens or creates text file for writing at end of file No additional effect in|out Opens file for update (read or write), with position at beginning of file Position is at end of file in|out|trunc Opens file for update, truncates to empty No effect on empty file With the binary flag added (...|binary), all conversions are suppressed.
This is like a binary data copy.
Possible ios_base Method Implementations
ios_base::fmtflags ios_base::flags () const { return flags; } ios_base::fmtflags ios_base::flags (ios_base::fmtflags newflags) { ios_base::fmtflags oldflags = flags; flags = newflags; return oldflags; } ios_base::fmtflags ios_base::setf (ios_base::fmtflags setbits) { ios_base::fmtflags oldflags = flags; flags |= setbits; return oldflags; } ios_base::fmtflags ios_base::setf (ios_base::fmtflags setbits, ios_base::fmtflags mask) { ios_base::fmtflags oldflags = flags; flags = (flags & ~mask) | (setbits & mask); // unsetf(mask); setf(setbits & mask); return oldflags; } ios_base::fmtflags ios_base::unsetf (ios_base::fmtflags unsetbits) { ios_base::fmtflags oldflags = flags; flags &= ~unsetbits; return oldflags; } char ios_base::fill (char newfill) { char oldfill = fill_character; fill_character = newfill; return oldfill; } int ios_base::precision () const { return precision_value; } int ios_base::precision (int val); { int oldprecision = precision_value; precision_value = val; return oldprecision; } int ios_base::width () const { return width_value; } int ios_base::width (int val) { int oldwidth = width_value; width_value = (val); return oldwidth; }
Class istream
namespace std { class istream : public class ios_base { // overloads of input operator friend istream& operator >> (istream& , char); friend istream& operator >> (istream& , int); friend istream& operator >> (istream& , long); friend istream& operator >> (istream& , unsigned char); friend istream& operator >> (istream& , unsigned int); friend istream& operator >> (istream& , unsigned long); friend istream& operator >> (istream& , float); friend istream& operator >> (istream& , double); friend istream& operator >> (istream& , long double); friend istream& operator >> (istream& , char* ); ... public: // methods to access next element in stream char get (); void get (char&); char peek (); ... } cin; // predefined object } // namespace std
Class ostream
namespace std { class ostream : public class ios_base { // overloads of output operator friend ostream& operator << (ostream& , char); friend ostream& operator << (ostream& , int); friend ostream& operator << (ostream& , long); friend ostream& operator << (ostream& , unsigned char); friend ostream& operator << (ostream& , unsigned int); friend ostream& operator << (ostream& , unsigned long); friend ostream& operator << (ostream& , float); friend ostream& operator << (ostream& , double); friend ostream& operator << (ostream& , long double); friend ostream& operator << (ostream& , const char* ); ... public: // method to send element to stream void put(char ch); ... } cout, cerr, clog; // predefined objects } // namespace std
Predefined Objects cin, cout, cerr, clog
- Predefined istream object: cin
- Predifined ostream objects: cout, cerr, clog
- Buffered ostreams: cout, clog
- Tied stream pair: cin, cout
cin.tie(&cout); // in file iostream
Class ifstream
class ifstream : public istream { public: ... ifstream* open ( const char* filename, ios_base::open_mode mode = ios_base::in ); // opens file in specified mode, sets fail bit on failure // returns this on success, NULL on failure ifstream* close (); // closes file previously opened with open() // fails immediately if no file is currently open // sets fail bit if any operation fails // returns this on success, NULL on failure ... };
Class ofstream
class ofstream : public ostream { public: ... ofstream* open ( const char* filename, ios_base::open_mode mode = ios_base::out | ios_base::trunc ); // opens file in specified mode, sets fail bit on failure // returns this on success, NULL on failure ofstream* close (); // flushes buffer, then closes file previously opened with open() // fails immediately if no file is currently open // sets fail bit if any operation fails // returns this on success, NULL on failure ... };
I/O Manipulators - 1
// these take no parameters, defined in <iostream> boolalpha; // calls s.setf(ios::boolalpha) noboolalpha; // calls s.unsetf(ios::boolalpha) showbase; // calls s.setf(ios::showbase) noshowbase; // calls s.unsetf(ios::showbase) showpoint; // calls s.setf(ios::showpoint) noshowpoint; // calls s.unsetf(ios::showpoint) showpos; // calls s.setf(ios::showpos) noshowpos; // calls s.unsetf(ios::showpos) uppercase; // calls s.setf(ios::uppercase) nouppercase; // calls s.unsetf(ios::uppercase) skipws; // calls s.setf(ios::skipws) noskipws; // calls s.unsetf(ios::skipws) unitbuf; // calls s.setf(ios::unitbuf) nounitbuf; // calls s.unsetf(ios::unitbuf) left; // calls s.setf(ios::left, ios::adjustfield) right; // calls s.setf(ios::right, ios::adjustfield) internal; // calls s.setf(ios::internal, ios::adjustfield) dec; // calls setf(ios::dec, ios::basefield) hex; // calls setf(ios::hex, ios::basefield) oct; // calls setf(ios::oct, ios::basefield) fixed; // calls setf(ios::fixed, ios::floatfield) scientific; // calls setf(ios::scientific, ios::floatfield) endl; // flushes streambuff and inserts '\n' ends; // flushes streambuff and inserts '\0' flush; // flushes streambuff
I/O Manipulators - 2
- Prototype for 0-parameter manipulators:
stream_type& manip_name (stream_type&);- User can define and use manipulators, e.g.:
// define: // use: ostream& beepbeep (ostream& os) std::cout << beepbeep << ... { os << "\a\a"; return os; }
I/O Manipulators - 3
- These manipulators take parameters and are more complicated to define
- the implementations use templates and function objects
- defined in <iomanip>
setbase (int b); // sets base (radix) for numericals // b = 8, 10, 16 sets oct, dec, hex flag, respectively // b = 0 means default: decimal on output, C rules for integers on input setiosflags (ios::format_flags mask); // calls ios::setf (mask) resetiosflags (ios::format_flags mask); // calls ios::unsetf (mask) setfill (char ch); // calls ios::fill (ch) setprecision (int n); // calls ios::precision (n) setw (int n); // calls ios::width (n)User may define a more extensive collection