Streams

Input and output. Except for binary I/O, this amounts to mapping objects from and to sequences of characters. I/O is implemented in C++ with a special set of classes. It is made to be type safe and extensible (unlike C's printf() and scanf()) with no compromise in flexibility.

The core I/O facilities are accessed using the include directive iostream. There are four special objects which are pre-constructed (std::cinstd::coutstd::clog and std::cerr) which handle standard input, output and two sorts of error streams. I/O is done via the overloaded operators << ("put to" or "insertion" operator for output to cerrclog andcout) and >> ("get from" or "extraction" for input from cin), or via member functions.

Additional I/O facilities are declared using the include directives iomanip (manipulators), fstream (files) and sstream (stringstreams -- streams based on strings).

8.1 C++ stream library class tree

C++ stream library class tree

Except for ios_base, all these classes are typedefs for template invocations. The templates all take two arguments: a character type and a class with information for manipulating the character type. In this case, the template arguments are char and char_traits<char>.

Inheritance from ios is shown with dashed lines since it is a virtual base class of its derived classes. This way the iostream class, which is defined using multiple inheritance, only gets one copy of the ios base class.

8.2 class ios_base

This is the root of all the stream classes. Error state and formatting information are included in class ios_base.

Error State Flags

The error state consists of four bits: ios_base::goodbitios_base::eofbitios_base::failbit and ios_base::badbit. Note in C, printf(), scanf() etc. were stateless. The stream state has important implications for I/O operations in C++. For example, in C if a formatted input operation fails, the operation can be repeated with another format or in unformatted mode and possibly succeed. In C++, the second and further operations, formatted or unformatted, will not succeed until the programmer explicitly resets the failure state resulting from the first operation.

The four bits representing a stream's state are stored in an int which can be read using:

int rdstate() const;

The state of the badbit could therefore be tested with an expression like:

if (cin.rdstate() & ios_base::badbit) // handle error ...
else // everything OK ...

A more convenient way of testing the individual bits is provided by the following member functions:

bool good() const;
bool eof() const;        // end of the stream encountered
bool fail() const;       // non-fatal error - failed to read expected data
bool bad() const;        // fatal error - stream can no longer be used
bool operator !() const; // returns true if failbit is set
operator void *() const; // returns non-NULL if failbit is not set

Note that operator!() and the void * converter give no indication whether eofbit has been set. eofbit can get set even when an input operation succeeds. For example, input of a number will set eofbit when the end of the stream directly follows the number. Subsequent input operations may fail, even if the stream is rewound, if the state of the stream is not set back to goodbit. The safest procedure is to explicitly clear() (see below) the stream state to continue input operations after a rewind of the stream in question even though no input operation has failed. The streamio example illustrates this issue as well as a variety of stream operations.

The error state can be set using:

void clear(iostate = 0);

The default value of zero results in ios_base::goodbit being set.

clear();

is therefore equivalent to

clear(0);

which is equivalent to

clear(ios_base::goodbit);

Note that ios_base::goodbit is a non-zero value. clear() might be used to set one of the other bits as part of a programmer's code for operator>>() for a particular object. For example:

if (bad_char) is.clear(ios_base::badbit); // set istream's badbit

Formatting Information

A fair amount of the formatting information is in the form of flags which are contained in an integer type declared in the standard library called std::fmtflags. These flags retain their settings until explicitly changed. The crudest access is:

fmtflags flags() const;   // reads the flags
fmtflags flags(fmtflags); // sets flags and returns previous setting

This sort of access should be used only to read the flags as a whole so they can be restored later. A function might do this if it needed specific formatting, but also needed to leave the state of the formatting flags of a stream argument unchanged.

Example:

void f(ostream &os)
{
  std::fmtflags flags = os.flags(); // record original state of format flags
  ...
  os.setf(ios_base::basefield, ios_base::oct);
  ...
  os.flags(flags); // restore original state of format flags
}

Control of individual flag settings can be achieved via:

fmtflags setf(fmtflags);   // set one or more individual flags
                           // (not a member of a group)
                           // or use setiosflags(fmtflags) manipulator
fmtflags unsetf(fmtflags); // reset one or more individual flags
                           // (not a member of a group)
                           // or use resetiosflags(fmtflags) manipulator

Both the setf() functions and unsetf() return the previous value of the flags. This applies to:

ios_base::skipws    // this is the only one that defaults to true
ios_base::boolalpha // insert/extract boolean type in alphabetic format
ios_base::showbase  // add 0 or 0x to non-decimal printouts
ios_base::showpoint // trailing zeros and decimal point always appear in floats
                    // formatted as if all trailing digits were non-zero
ios_base::showpos   // adds + sign to positive values
ios_base::uppercase // use X, A-F for hex and E for exponential
ios_base::unitbuf   // flush ostream after each output operation

A two argument version of setf() is used to set flag which is a member of a group of flags. The second argument specifies the group and is a bitwise OR of all the flags in the group. The specified bit is set and the rest are unset. This function is:

fmtflags setf(fmtflags, fmtflags group);

The groups are:

ios_base::adjustfield  // padding position
  ios_base::left       // left aligned
  ios_base::right      // right aligned (this is the default)
  ios_base::internal   // between sign or base and value

ios_base::basefield    // or use setbase(int = 0, 10, 8 or 16) manipulator
  ios_base::dec        // or use dec manipulator (default)
  ios_base::oct        // or use oct manipulator
  ios_base::hex        // or use hex manipulator

ios_base::floatfield   // neither flag is set by default -- general format
  ios_base::scientific
  ios_base::fixed

The last group is a little odd in that it makes sense for both flags to be unset. This is a shadow "automatic" state and is comparable to the 
%g
 format of printf() where the format shifts between scientific and fixed depending on which is the most compact representation.

Other formatting information is set by the following functions.

char fill(char);   // set fill char; or use setfill(char) manipulator
char fill() const; // find current value of fill char (default is ' ')
streamsize precision(streamsize); // number of floating point digits displayed
streamsize precision() const;     // or use setprecision(streamsize) manip
                                  // (default is 6)
streamsize width(streamsize); // or use setw(streamsize) manip
streamsize width() const;     // default is 0 (no padding space)

For width(), this information is temporary, and the default width (0) is returned to after a field is inserted.

The effect of the ios_base::precision(streamsize) method differs depending on which of the three possible ios_base::floatfield states governs floating-point formatting. In the default "automatic" state when neither bit is set, it represents the total number of digits used. When ios_base::fixed is set, it is the number of digits after the decimal point. When ios_base::scientific is set, it is the number of digits in the mantissa.

Two miscellaneous capabilities:

A method for tying an istream to an ostream is available so that the ostream gets flushed before any input operation. cout and cin are tied by default. The ostream *ios_base::tie(ostream *) method takes and returns a pointer to an ostream. The pointer returned is the previous tie. Tying to 0 breaks any existing tie. It can only be tied toone ostream at a time.

8.3 Input

Pre-defined object cin (class istream with public base ios).

Result of >> operator is istream &. In combination with left-to-right associativity of >>, this means the right thing happens. For example:

cin >> x >> y;

Notice that, though non-const objects must be used, pointers are not used as in C since >> is overloaded using reference arguments. There is also an ios converter (to void *) which allows istream objects to appear as control expressions. It converts to 0 pointer if fail or bad bit is set. fail flag must be cleared to continue input (bad flag set in addition indicates more fundamental problem). Use clear() member function to do this:

int x; char c;

while (cin) // get stream of integer values
{
  while (cin >> x) { cout << x; process(x); }
  cin.clear();
  while (cin.get(c) && c != '\n'); // flush line from stream on error
}

Without the cin.clear() call, cin.get(c) would just be a no-op. This is different from the behavior of C's scanf() routine where succeeding calls are not affected by failures in previous calls. As in C, a failed operation must be registered before flag is set, so it should be tested after an input operation, not before.

Member functions:

istream(streambuf *);
istream &ignore(streamsize, int_type = EOF);
int_type peek();
istream &putback(char &);
int_type get(); // like C getchar();
istream &unget(); // putback most recent char read
istream &get(char &);
istream &get  // always terminates buffer with '\0'
              // doesn't extract terminator char from stream
(char *, streamsize, char = '\n');
istream &getline // same except extracts
                 // terminator char from stream
(char *, streamsize, char = '\n');
istream &read(char *, streamsize); // binary input
streamsize readsome(char *, streamsize); // binary input
istream &seekg(streampos);  // set position indicator
istream &seekg(streamoff, seek_dir); // dir is beg, cur or end
streampos tellg() const; // g suffixes stand for "get"
streamsize gcount() const; // number of chars extracted by last unformatted
                           // input function (get, getline, ignore, read)

8.4 Notes on random access

Given the behavior of the C stdio routines seek() and tell(), which work on all files irregardless of whether they are opened for just reading, just writing or reading and writing, one might expect to find seek/tell routines declared in class ios, but this is not the case. In C++, separate separate positions are maintained for doing input and output. During read/write access, a program can be reading at one position in a file and writing at another position. So in C++, to get this finer-grained functionality, the seekx/tellx methods are supported at the istream and ostream level rather than at the ios level.

State can be important to consider when re-reading data from a stream in C++. For example, if a program reads numbers from a stream until failure to see how many numbers a file contains, when it rewinds the stream (via seekg(0, ios::beg)) it must make sure to clear() the stream before attempting to read again; otherwise, the read attempts on the second pass will fail since the ios_base::failbit is set due to the read failure at the end of the initial counting pass. In C stdio, this is not an issue.

8.5 Output

Pre-defined objects cerrclog and cout (class ostream with public base ios). Note cerr is not line buffered as in C while cout retains line buffering. clog is line buffered and is an alternative interface to the error stream.

In printing expressions, be careful to use parens if expressions' operators have greater or equal precedence compared with <<. Result of << operator is ostream &. In combination with left-to-right associativity of << this means the right thing happens with (for example):

cerr << "x = " << x;

The type of character constants is char in C++ version 2.0 and after, not int as in C and C++ version 1.0. Putting a char to an ostream results in the character corresponding to the code being printed. To get the integer code printed, the character must be cast to an int.

User-defined types are output by overloading the << operator. Since an ostream & is the first argument for this binary operator, it can't be implemented as a member function of the object being output, but is done using a free-standing function whose second argument is the object being output.

Implementing input for user-defined types is like output except it may be appropriate to change the state of the input stream if an operations fails. For example, complex input routine to get a complex value in the form (2.2, 3.3) would fail and set the bad bit if the sequence of characters retrieved from the istream don't fit the prescribed format. Perhaps setting the fail bit would be appropriate if it had saved the characters and put them back in the event of a failure. The ios_base::clear() function is used to set the state of a stream.

Member functions:

ostream &put(char);
ostream &flush();
ostream &write(const char *, streamsize);   // binary output
ostream &seekp(streampos),
ostream &seekp(streamoff, seek_dir); // seek_dir is ios_base::beg,
                                     // ios_base::cur or ios_base::end
streampos tellp() const;  // p suffixes stand for "put"
operator <<(streambuf *); // transfers characters to its own streambuf
                          // from this one until it can't find any more

8.6 Files (devices)

The header fstream.h contains definitions for fstream (derived from iostream which is in turn derived from istream and ostream), ofstream (derived from ostream) and ifstream. These are constructed with a character string containing the name of the file and an optional mode composed of bitwise-ORd flags derived from an enum defined in ios. Flags are:

ios_base::in allow input from stream
ios_base::out allow output to stream
ios_base::ate put and get pointers are initially set to the end of the file; however, they can be changed using seekg() or seekp()

(doesn't imply ios_base::out )

ios_base::app new output is always appended to the end of the file, even if seekp() is used

(implies ios_base::out )

ios_base::trunc delete any existing file

implied when ios_base::out used without ios_base::ate or ios_base::app

ios_base::binary binary file (don't convert <CR><LF> combinations to/from single newline character) - there were problems with UNIX compilers supporting this, but it is now part of the ANSI draft

These classes have a close() method which only need be invoked if the file is to be closed before the object goes out of scope. This allows the object to be used in a loop to open many files in succession.

Member functions (x below can be replaced by of, if or f):

xstream(); // postpone opening file
xstream(const char *, openmode mode); // check fail() bit to see if successful
void open(const char *, openmode mode);
void close();

The mode defaults are ios_base::in for ifstreams, ios_base::out for ofstreams and no default for fstreams.

8.7 string I/O

I/O facilities using C++ strings are declared using the sstream include directive. This declares ostringstream (derived from ostream), istringstream (derived from istream) andstringstream (derived from iostream). Note below the useful no-argument constructor for an ostringstream. This class is especially useful for constructing labels and log messages.

Selected member functions:

istringstream(string);
ostringstream(string, ios_base::openmode = ios_base::out); // see above
ostringstream(); // dynamically allocated internal buffer
stringstream(string, ios_base::openmode = ios_base::out); // see above
stringstream(); // dynamically allocated internal buffer
string str() const; // get copy of internal buffer
void str(string);   // set internal buffer to copy of string

8.8 Mixing in C I/O

C I/O functions can be intermixed with C++ I/O portably on a per-character basis. A call to ios_base::sync_with_stdio() before the first I/O operation resets cin, cout, cerr andclog to share buffers with the corresponding C objects stdin, stdout and stderr.

8.9 Manipulators

Besides invoking stream methods, another way of performing operations on streams is to "put to" or "get from" with special objects called manipulators. Using manipulators rather than methods for operating on streams can give code using streams a more stream-like look. The standard library manipulators are accessed using the iomanip include directive. Two examples of code for printing a table contrast the method style of operating on streams with the manipulator style.

Method style:

for (i = 0; i < size; i++)
{
  std::cout.setwidth(ID_WIDTH);
  std::cout.setf(std::ios_base::left, std::ios_base::adjustfield);
  std::cout << table.line(i).id();
  std::cout.setwidth(COUNT_WIDTH);
  std::cout.setf(std::ios_base::right, std::ios_base::adjustfield);
  std::cout << table.line(i).count();
  std::cout.setwidth(WEIGHT_WIDTH);
  std::cout << table.line(i).weight() << '\n';
}

Manipulator style:

for (i = 0; i < size; i++)
{
  std::cout << std::setw(ID_WIDTH) << std::left << table.line(i).id()
            << std::setw(COUNT_WIDTH) << std::right << table.line(i).count()
            << std::setw(WEIGHT_WIDTH) << table.line(i).weight()
            << std::endl;
}

Both styles are a lot more verbose than the succinct style we were used to in C. The manipulator style is easier to look at and use than the method style most of the time. Even if more verbose, the manipulator style is easier to look at, easier to use and safer than the old C style I/O. Notice that nothing needed to be said in the code above about the types of the data.

Manipulators (for example std::setw()std::leftstd::right and std::endl in the example above) are objects which, when "put to" (<<) or "gotten from" (>>) a stream, insert/extract data from the stream and/or change the formatting state of the stream. No-argument manipulators are typically function pointers. The standard library overloads "put to" and "get from" so any pointer to a function of the form

ios_base &f(ios_base &)
results in the stream being passed to the function in question when << or >> are applied to the bare function name (which amounts to a pointer to the function). The standard library has no-argument manipulators defined for all the flags we discussed.

Implementation of manipulators with arguments is more work. An example of such a manipulator from the standard library is:

cout << setprecision(4) << angle;
For this syntax to work, three things must be done:

  1. Write the definition for a function setprecision(streamsize) which constructs and returns a reference to an object. For this example, let's call the type of the objectsmanip.
  2. Write a class definition for the smanip type. When implementing a family of manipulators, it may be convenient to have this type generated using a template.
  3. Overload << so that putting an object of type smanip to an ostream results in ios_base::precision(streamsize) being invoked for the ostream using integer value specified when the smanip was constructed.

Multi-argument or single-argument manipulators can be defined using this method.

http://www.trip.net/~bobwb/cppnotes/lec08.htm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值