Software Design Using C++

An Online Book

Old Version -- No Longer Maintained

Please Refer to the New Version

Reading a Data Stream

Introduction

Often we want a program to process a long sequence of data. In this Web page we will only consider the processing of a sequence of numbers. These numbers can be read from the keyboard (that is, interactively from the user) or from a text file. Other types of data and other types of file handling are discussed in the intermediate section of these Web pages under the heading Files (Streams).

The general idea is that we will read in data until we cannot read any more. If we are reading the data from a file, we could say that we are reading data until we reach the end of the file (eof, for short). In reading data interactively from the keyboard, we must instruct the user to press CTRL z to signal the end of the data. (This means to hold down the CTRL key and touch the z.) CTRL z is essentially the eof signal for interactive input.

Reading from the Keyboard

Look at the program readkeyb.cpp to see how to read a stream of numbers from the keyboard. This program reads in a stream of floating point numbers and then prints out their average. Part of the code is shown below. The initialization of the variables and the printing of the average have been left out here in order to save space.

cout << "Enter a floating point number (or CTRL z to end): ";
cin >> Num;

while (! cin.fail())
   {
   Total = Total + Num;
   Count++;
   cout << "Enter a floating point number (or CTRL z to end): ";
   cin >> Num;
   }

The only thing really new here is the use of cin.fail() to control the WHILE loop. The stream cin refers to input from the keyboard. The fail function, when applied to cin, tells us if the last operation on cin failed. This could happen if the user entered something that is not a number, such as the letter A. It would also happen if the user pressed CTRL z to signal the end of the data stream. The latter would be the normal way for this loop to end. Try both of these when you compile and run this program.

Remember that the ! symbol is the boolean NOT operation. (See the discussion of Compound Conditions in the Web page about decisions for more information on boolean operations.) The overall idea in the above code is that we go through the loop body as long as the preceding input operation did not fail.

There is also an eof function that can tell us if we are at the end of a stream of data (in this example that would mean that we have received a CTRL z). However, this function only checks for end of file; it does not check for non-number data like the letter A. Thus it is better to use the fail function as it stops the loop reasonably in both cases. Note, too, that with some compilers the eof function appears not to work reliably. So for both reasons use the fail function.

Notice that cin is really an object and that we are using the usual object-oriented syntax in calling a class function on an object. (See Using Objects for more information on objects.) Compare the specific function call with the general syntax:

cin.fail()
object.function(parameter_list)

Reading from a Text File

See the program readfile.cpp for a simple example of how to read numbers from a text file until the end of file has been reached. The program reads floating point numbers and prints the average once end of file has been reached, much like the above example.

The main difference is that this program reads the numbers from a text file. A text file is the type of file consisting of lines of characters. (See Files (Streams) for more information on types of files.) A text file can be created by NotePad in Windows, Edit in DOS, etc. There is even an icon for creating a new text file in Visual C++ Developer Studio. In fact, our program files are text files themselves, although their names end with the .cpp extension instead of the usual .txt extension used by most text files on a PC.

According to the comments at the top of the program, the numbers in the text file must be placed on separate lines or separated by blank space. That is how the different numbers are distinguished.

Near the top of the program you will see the inclusion of some new headers. The fstream header is the one we will use whenever we work with files.

Below we have pasted in the entire main function, the only function in this program. Note that it creates a variable called InFile of type fstream. This fstream type is the one we will use for all of our files throughout all of these Web pages.

int main(void)
   {
   fstream InFile;
   float Num, Total;
   int Count;

   InFile.open("readfile.txt", ios::in);
   if (InFile.fail())
      {
      cout << "Could not open readfile.txt" << endl;
      exit(1);
      }

   Count = 0;
   Total = 0.0;

   InFile >> Num;

   while (! InFile.fail())
      {
      Total = Total + Num;
      Count++;
      InFile >> Num;
      }

   if (Count > 0)
      cout << "Average is " << Total / Count << endl;
   else
      cout << "No data given" << endl;

   InFile.close();
   return 0;
   }

The open function applied to the InFile variable (object) is an important one. This is how one indicates what file is to be accessed and what type of access is desired. The file in this case is one named readfile.txt. The ios::in indicates that we are opening the file for input (that is, in order to read data from it). The variable InFile is used from here on in the program whenever we need to refer to this file.

InFile.open("readfile.txt", ios::in);
if (InFile.fail())
   {
   cout << "Could not open readfile.txt" << endl;
   exit(1);
   }

Right after we try to open the file, there is a test of InFile.fail(). Much like in the previous program, the fail function is used to tell us if the last operation on the data stream failed. Here this is the open command itself. You should always check to see if an open failed and if so, print an error message. In this program, we even exit from the program if the open failed. There is no sense in trying to go on with our program if we cannot access the data file.

Why would open ever fail? This would happen if the file is not present in the expected directory (normally the same directory where the program is, if running from Windows, or the project directory, if running from Visual Studio).

The heart of the program is the same as in the previous example, except that the data is read from InFile instead of cin. Thus you see the use of InFile >> Num to read a number from InFile. You also get the same overall WHILE loop as in the previous program. The only difference is that the fail function is being used to see if the last input operation on InFile worked, instead of the last input operation on cin. We read data until reading fails.

InFile >> Num;

while (! InFile.fail())
   {
   Total = Total + Num;
   Count++;
   InFile >> Num;
   }

After computing and printing the average, the program ends by closing the data file. Get into the habit of always closing your data files as soon as you are done with them. With some languages and compilers this is not necessary, but with others it is, so it is best to always close your files. Besides, it consumes some computer resources to keep a file open, so it is best to close it as soon as possible. The command for closing InFile is:

InFile.close();

There are other ways to set up files instead of using fstream. The type ifstream can be used for input files. But then we need a different type for output files. Plus, in some programs we will want to have a file open for reading and writing both! All in all, it will be easier to use fstream consistently for all of our files.


Back to the main Software Design Page