`
huozheleisi
  • 浏览: 1235400 次
文章分类
社区版块
存档分类
最新评论

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'sprintf()andscanf()) with no compromise in flexibility.

The core I/O facilities are accessed using the include directiveiostream. There are four special objects which are pre-constructed (std::cin,std::cout,std::clogandstd::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 tocerr,clogandcout) and>>("get from" or "extraction" for input fromcin), or via member functions.

Additional I/O facilities are declared using the include directivesiomanip(manipulators),fstream(files) andsstream(stringstreams -- streams based onstrings).

8.1 C++stream library class tree

C++ stream library class tree

Except forios_base, all these classes aretypedefs 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 arecharandchar_traits<char>.

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

8.2 classios_base

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

Error State Flags

The error state consists of four bits:ios_base::goodbit,ios_base::eofbit,ios_base::failbitandios_base::badbit. Note in C,printf(), scanf()etc. werestateless. 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 anintwhich can be read using:

int rdstate() const;

The state of thebadbitcould 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 thatoperator!()and thevoid*converter give no indication whethereofbithas been set.eofbitcan get set even when an input operation succeeds. For example, input of a number will seteofbitwhen 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 togoodbit. The safest procedure is to explicitlyclear()(see below) the stream state to continue input operations after a rewind of the stream in question even though no input operation has failed. Thestreamio exampleillustrates 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 inios_base::goodbitbeing set.

clear();

is therefore equivalent to

clear(0);

which is equivalent to

clear(ios_base::goodbit);

Note thatios_base::goodbitis a non-zero value.clear()might be used to set one of the other bits as part of a programmer's code foroperator>>()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 calledstd::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 thesetf()functions andunsetf()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 ofsetf()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 ofprintf()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)

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

The effect of theios_base::precision(streamsize)method differs depending on which of the three possibleios_base::floatfieldstates governs floating-point formatting. In the default "automatic" state when neither bit is set, it represents the total number of digits used. Whenios_base::fixedis set, it is the number of digits after the decimal point. Whenios_base::scientificis set, it is the number of digits in the mantissa.

Two miscellaneous capabilities:

A method for tying anistreamto anostreamis available so that theostreamgets flushed before any input operation.coutandcinare tied by default. Theostream *ios_base::tie(ostream *)method takes and returns a pointer to anostream. The pointer returned is the previous tie. Tying to 0 breaks any existing tie. It can only be tied tooneostreamat a time.

8.3 Input

Pre-defined objectcin(classistreamwith public baseios).

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

cin >> x >> y;

Notice that, though non-constobjects must be used, pointers are not used as in C since>>is overloaded using reference arguments. There is also aniosconverter (tovoid *) which allowsistreamobjects to appear as control expressions. It converts to 0 pointer iffailorbadbit is set.failflag must be cleared to continue input (badflag set in addition indicates more fundamental problem). Useclear()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 thecin.clear()call,cin.get(c)would just be a no-op. This is different from the behavior of C'sscanf()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 Cstdioroutinesseek()andtell(), 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 classios, 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 theistreamandostreamlevel rather than at theioslevel.

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 (viaseekg(0,ios::beg)) it must make sure toclear()the stream before attempting to read again; otherwise, the read attempts on the second pass will fail since theios_base::failbitis set due to the read failure at the end of the initial counting pass. In Cstdio, this is not an issue.

8.5 Output

Pre-defined objectscerr,clogandcout(classostreamwith public baseios). Notecerris not line buffered as in C whilecoutretains line buffering.clogis 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 isostream &. 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 ischarin C++version 2.0 and after, notintas in C and C++version 1.0. Putting acharto anostreamresults in the character corresponding to the code being printed. To get the integer code printed, the character must be cast to anint.

User-defined types are output by overloading the<<operator. Since anostream &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,complexinput routine to get acomplexvalue in the form(2.2, 3.3)would fail and set thebadbit if the sequence of characters retrieved from theistreamdon't fit the prescribed format. Perhaps setting thefailbit would be appropriate if it had saved the characters and put them back in the event of a failure. Theios_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 headerfstream.hcontains definitions forfstream(derived fromiostreamwhich is in turn derived fromistreamandostream),ofstream(derived fromostream) andifstream. These are constructed with a character string containing the name of the file and an optional mode composed of bitwise-ORd flags derived from anenumdefined inios. 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 usingseekg()orseekp()

(doesn't implyios_base::out)

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

(impliesios_base::out)

ios_base::trunc delete any existing file

implied whenios_base::outused withoutios_base::ateorios_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 aclose()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 (xbelow can be replaced byof, iforf):

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

Themodedefaults areios_base::inforifstreams,ios_base::outforofstreams and no default forfstreams.

8.7stringI/O

I/O facilities using C++strings are declared using thesstreaminclude directive. This declaresostringstream(derived fromostream),istringstream(derived fromistream) andstringstream(derived fromiostream). Note below the useful no-argument constructor for anostringstream. 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 toios_base::sync_with_stdio()before the first I/O operation resetscin, cout, cerrandclogto share buffers with the corresponding C objectsstdin, stdoutandstderr.

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 theiomanipinclude 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 examplestd::setw(),std::left,std::rightandstd::endlin 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 functionsetprecision(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 thesmaniptype. 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 typesmanipto anostreamresults inios_base::precision(streamsize)being invoked for theostreamusing integer value specified when thesmanipwas constructed.

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

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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics