Previous Table of Contents Next


The example program calls the fgetc function to read from the file. This function reads a single character from the input stream and is the file-input version of getchar, which reads a single character from standard input. The character read is compared to EOF, a constant defined in STDIO.H that indicates the end-of-file condition.

Line 28 calls fgetc. This complex line of code does the following: it calls fgetc to get a character; it assigns the result to the integer variable, c; and it compares that value to EOF. As long as the value returned is not EOF (the end of file has not yet been reached), the loop continues.

      while ((c = fgetc (fp)) != EOF)
      ...
All the file operations in this chapter assume sequential access with text-based I/O. Such access techniques treat files as streams similar to the screen and keyboard. This access mode is often easiest to program, because it grows out of the standard I/O techniques (printf, scanf, and so on) that most beginners start with.
However, the logic of some programs requires random access: reading and writing fixed-length records to numbered or indexed locations in a file. (Note that there is no underlying hardware difference between a random access file and a sequential file; they are simply treated a little differently by a program.) Random access is not intrinsically more difficult. Generally, you open a random access file in binary mode and use the following library functions: fread, fwrite, and fseek. Although C++ has no notion of a record number, you can easily seek (move) to the appropriate position in a file by multiplying the record number by the size of each record. Records, in turn, are implemented as C++ structures or classes.

Files and Stream Operators

The stream concept can be applied to disk files as much as to the screen and keyboard. C++ supports objects of type ifstream (input-file stream) and ofstream (output-file stream). You create these objects by specifying the name during initialization. First, however, include the file FSTREAM.H. This file automatically includes all the contents of IOSTREAM.H.

#include <fstream.h>

To create file-stream objects, specify the name in parentheses when you define the object. For example:

ifstream inf (“C:\\TXT\\DATA.TXT”);
ofstream outf (“C:\\TXT\\OUTPUT.TXT”);

As always when specifying full path names, remember to use double backslashes inside C quoted strings whenever you’re indicating a single backslash. Once the objects are defined and initialized with full or relative path names, you can use them just as you can cin and cout.

outf << “Here is a string written to a file.”

long n;
inf >> n;

Stream objects evaluate to null values if an error condition, such as end-of-file, is present. So you can write loops such as the following, which stops after text has been exhausted.

while (inf)
   inf.getline (string, 80);

The getline function is a member function of the inf object; getline provides the same functionality that gets and fgets do. The first argument is a character string; the second is the maximum number of characters to read.

The following program prints the contents of the file DATA.TXT

#include <fstream.h>

void main() {
   char buffer [81];

// open input file DATA.TXT, in current directory.

   ifstream inf (“DATA.TXT”);

// While end of file not reached, get and print
// a line of text, appending a newline each time.

   while (inf) {
       inf.getline (buffer, 80);
       cout << buffer << “\n”;
   }
}

The Great Controversy: To Stream or Not to Stream

The major theme of this chapter has been that when it comes to input and output, there is more than one way to do things. Most books on C++ are strongly oriented toward one or the other approach and tend to dismiss the approach not used as either wrong-headed or old-fashioned.

There’s a good reason for this (it’s not only that authors are a stubborn, biased lot!) as I’ve pointed out, you should not mix different input/output techniques, such as using cin on one line and scanf on the next. Doing so is almost guaranteed to cause errors. Generally, C++ authors find it easiest to take the position that one approach is much better and stick with that. That way they don’t encourage the errors that would arise from mixing the approaches.

The truth is that you can write equally efficient, readable, and elegant code either way. The stream operators are, arguably, simpler to use in trivial cases (although they introduce objects, which may confuse beginners). Using printf and scanf usually makes for more-readable code when significant formatting is involved, although the format function brings the blessings of printf format specifiers to cout.

If you have a C background and are comfortable with printf and scanf, there’s no reason not to go on using them. For the next several chapters, I’ll stick with printf and scanf in examples. I easily could have used cin and cout instead, but I went with printf and scanf to make the material a little more accessible to C programmers.

The stream objects are interesting because they quickly introduce classes and objects into the daily, routine tasks of programming. If you don’t have piles of C legacy code to maintain and you are chomping at the bit to be object-oriented, you will likely want to begin using cin and cout immediately. The advantage of stream operators is that you can overload them for your own classes; this capability demonstrates bow the object-oriented approach is inherently more extensible than traditional functions such as printf are. You cannot write a new format specifier for printf, for example, but using operator overloading, you can extend C++ so that it knows how to print objects of your own classes (by sending them to cout):

CMyClass myobject;
cout << “Here is my object: ” << myobject << “\n”

We’ve gotten way ahead of the story. Before I can explain how to overload the operator, you need to confront the mysteries of classes, objects, constructors, and member functions, which lie ahead in the next few chapters. This has been a transitional chapter. With any luck, it has aroused your curiosity about objects and their role in C++.

Once you understand operator overloading, see the topics “istream Class” and “ostream Class” in Part III. for an explination of how to extend your classes to support stream operations.


Previous Table of Contents Next