Выбрать главу

int i;

while (myStream >> i)

  cout << i << endl;

Remember that operator>>( ) returns its stream argument, so the while statement above tests the stream as a Boolean expression. This particular example assumes that the input stream myStream contains integers separated by white space. The function ios_base::operator void*( ) simply calls good( ) on its stream and returns the result.[41] Because most stream operations return their stream, using this idiom is convenient.

Streams and exceptions

Iostreams existed as part of C++ long before there were exceptions, so checking stream state manually was just the way things were done. For backward compatibility, this is still the status quo, but iostreams can throw exceptions instead. The exceptions( ) stream member function takes a parameter representing the state bits for which you want exceptions to be thrown. Whenever the stream encounters such a state, it throws an exception of type std::ios_base::failure, which inherits from std::exception.

Although you can trigger a failure exception for any of the four stream states, it’s not necessarily a good idea to enable exceptions for all of them. As Chapter 1 explains, use exceptions for truly exceptional conditions, but end-of-file is not only not exceptional—it’s expected! For that reason, you might want to enable exceptions only for the errors represented by badbit, which you would do like this:.

myStream.exceptions(ios::badbit);

You enable exceptions on a stream-by-stream basis, since exceptions( ) is a member function for streams. The exceptions( ) function returns a bitmask[42] (of type iostate, which is some compiler-dependent type convertible to int) indicating which stream states will cause exceptions. If those states have already been set, an exception is thrown immediately. Of course, if you use exceptions in connection with streams, you had better be ready to catch them, which means that you need to wrap all stream processing with a try block that has an ios::failure handler. Many programmers find this tedious and just check states manually where they expect errors to occur (since, for example, they don’t expect bad( ) to return true most of the time anyway). This is another reason that having streams throw exceptions is optional and not the default. In any case, you can choose how you want to handle stream errors.

File iostreams

Manipulating files with iostreams is much easier and safer than using stdio in C. All you do to open a file is create an object; the constructor does the work. You don’t have to explicitly close a file (although you can, using the close( ) member function) because the destructor will close it when the object goes out of scope. To create a file that defaults to input, make an ifstream object. To create one that defaults to output, make an ofstream object. An fstream object can do both input and output.

The file stream classes fit into the iostreams classes as shown in the following figure.

As before, the classes you actually use are template specializations defined by type definitions. For example, ifstream, which processes files of char, is defined as.

typedef basic_ifstream<char> ifstream;

A File-Processing Example

Here’s an example that shows many of the features discussed so far. Notice the inclusion of <fstream> to declare the file I/O classes. Although on many platforms this will also include <iostream> automatically, compilers are not required to do so. If you want portable code, always include both headers.

//: C04:Strfile.cpp

// Stream I/O with files

// The difference between get() & getline()

#include <fstream>

#include <iostream>

#include "../require.h"

using namespace std;

int main() {

  const int sz = 100; // Buffer size;

  char buf[sz];

  {

    ifstream in("Strfile.cpp"); // Read

    assure(in, "Strfile.cpp"); // Verify open

    ofstream out("Strfile.out"); // Write

    assure(out, "Strfile.out");

    int i = 1; // Line counter

    // A less-convenient approach for line input:

    while(in.get(buf, sz)) { // Leaves \n in input

      in.get(); // Throw away next character (\n)

      cout << buf << endl; // Must add \n

      // File output just like standard I/O:

      out << i++ << ": " << buf << endl;

    }

  } // Destructors close in & out

  ifstream in("Strfile.out");

  assure(in, "Strfile.out");

  // More convenient line input:

  while(in.getline(buf, sz)) { // Removes \n

    char* cp = buf;

    while(*cp != ':')

      cp++;

    cp += 2; // Past ": "

    cout << cp << endl; // Must still add \n

  }

} ///:~

The creation of both the ifstream and ofstream are followed by an assure( ) to guarantee the file was successfully opened. Here again the object, used in a situation in which the compiler expects a Boolean result, produces a value that indicates success or failure.

The first while loop demonstrates the use of two forms of the get( ) function. The first gets characters into a buffer and puts a zero terminator in the buffer when either sz-1 characters have been read or the third argument (defaulted to '\n') is encountered. The get( ) function leaves the terminator character in the input stream, so this terminator must be thrown away via in.get( ) using the form of get( ) with no argument, which fetches a single byte and returns it as an int. You can also use the ignore( ) member function, which has two default arguments. The first argument is the number of characters to throw away and defaults to one. The second argument is the character at which the ignore( ) function quits (after extracting it) and defaults to EOF.

Next, you see two output statements that look similar: one to cout and one to the file out. Notice the convenience here; you don’t need to worry about what kind of object you’re dealing with because the formatting statements work the same with all ostream objects. The first one echoes the line to standard output, and the second writes the line out to the new file and includes a line number.

вернуться

41

It is customary to use operator void*(В ) in preference to operator bool(В ) because the implicit conversions from bool to int may cause surprises, should you errantly place a stream in a context where an integer conversion can be applied. The operator void*(В ) function will only implicitly be called in the body of a Boolean expression.

вернуться

42

An integral type used to hold single-bit flags.