Source Code. Projects. Nerd Stuff. Art Stuff.

Lesson 4: File I/O

File I/O

File I/O (Chapter 5)

This should be review, so we’ll go through this material pretty quickly. Stop me if you’re confused.

Later in this class we’ll define our own classes and abstract data types. But for file i/o, we use some pre-defined classes. So we need a few definitions:

class – a datatype to define an object.
object – a variable storing an instance of a class. An object is a special kind of variable that is like a package containing many things inside. It has multiple values inside it, and it also can have functions as part of it. These functions are called member functions.

To work with file i/o we need to include the fstream library with the statement:

1
  #include <fstream>

This allows us access to the ifstream class for input from a file, and the ofstream class for output to a file. We declare an input file stream with the following statement:

1
  ifstream file_in;

Then we open the file and verify that it opened correctly:

1
2
3
  file_in.open("scorefile.txt"); // Your filename here
if (file_in.fail())
// handle it, e.g. tell user we couldn't open file, exit program

Once a file stream is open we use it in much the same way as we use cin and cout. So to input a value from a file that is whitespace-delimited, we write:

1
2
  float one_score;
file_in >> one_score;

Or to input the next character including whitespace or anything else, we write:

1
2
  char c;
file_in.get(c);

And to test whether we’ve attempted to go past the end of the file yet, we use the member function eof:

  file_in.get(c);  // Get a character to start with
  while (!file_in.eof()) // Loop while we haven't passed end of file
  {
    cout << c;        // Output the character to the screen
    file_in.get(c);   // Get the next character from the file
  }

The above code will display on the screen the exact contents of the file, including whitespace. Another way of detecting the end of the file is to use the input expression as our loop condition. So to output all the numbers from a whitespace-delimited file onto the screen:

  while(file_in >> one_score)
    cout << one_score << " ";

The “putback” member function allows us to ‘back up’ in the file stream. So if we inputted a character, but want to input it a different way, we can put it back to be read again:

  file_in.get(c);  // Get a character to start with
  while (!file_in.eof() && c != '\n') // Stop at end of file or line
  {
    file_in.putback(c); // If it's not a newline, it may be part of a score
    // Input the next score into a float variable so it's easier to manipulate:
    file_in >> one_score;
    // Display each score, followed by a space:
    cout << one_score << " ";
    file_in.get(c);  // Get the next character to see if it's a newline
  }

Similar to the ifstream class, the ofstream class is used for file output.

For greater control over the format of file or screen output, use the library iomanip. This gives you access to functions to control decimal precision, output width and alignment, etc.:

  #include <iomanip>

  // Initialization of program skipped here

    ofstream out_stream;

    // Skipped here: opening of output file and input file declarations and initializations

    // standard "fixed" notation instead of scientific notation,
    // also making "precision" below set number of digits after
    // the decimal point, not significant digits:
    out_stream.setf(ios::fixed);
    // show all specified digits after decimal, even if 0's:
    out_stream.setf(ios::showpoint);
    // show 2 digits after decimal for floating pt. numbers:
    out_stream.precision(2);
    // Output one_score in a width of 5 characters (right-aligned):
    out_stream << setw(5) << one_score;

Equivalent to setw(5) is the stand-alone command:

1
  out_stream.width(5);

Remember that whichever way you set the width, it will only affect the one output immediately following it. The output width always reverts back to 0 (normal output).

All of the above output stream modifiers can be used the same way with the cout object, to affect how screen output is displayed.

Remember that to pass input and output stream objects as parameters to functions, you should always use call-by-reference:

1
  void handle_line_of_scores(ifstream&amp; fin, ofstream&amp; format_out);

Including the library cctype gives us access to predefined character functions such as isspace and tolower, which can be very handy. They’re listed in a table on p. 280 of your book (the end of Sec. 5.3)

Inheritance and Polymorphism – Section 5.4

One of the major advantages of object oriented programming is what’s called polymorphism. To understand what that is, let’s look at some definitions:
Inheritance – When one class is derived from another. This is the “a-kind-of” relationship — when a specific class is defined which is a kind of a more general class of objects (e.g. a circle is a kind of shape).
Parent or Super-class – A class whose member variables and functions are inherited by another class (by its child class).
Child or Derived Class, or Sub-class – A class which inherits some of its member variables and functions from another class (from its parent class)
Polymorphism- The ability to treat a class like its parent class. So a function can be written to take a parameter of a particular parent class, but this function may be called with an argument of any of that class’s derived classes. Since the derived classes get some of their behavior from their parent, this behavior can be used in a general way by such a function. For examples see the relationship between the classes istream and ifstream.

The standard input object cin is of class istream. Furthermore, istream is the parent class of ifstream. So an ifstream is a kind of istream, and we can treat an ifstream as if it were an istream. Check out the following function:

  // Get and return the next number from the input stream (ifstream object or cin),
  // skipping all non-numeric characters before that next number.
  double getNextNum(istream& instream)
  {
    char c;
    double num;

    do
    {
      instream.get(c); // read one character at a time
    }while(!isdigit(c));  // keep reading chars until we get a digit

    instream.putback(c);  // put that first digit back into the stream.
    instream >> num;  // read the entire number into the numeric variable num
    return num;
  }

The getNextNum function above can be called with either cin or an ifstream object as its argument. So both of the following calls to getNextNum are allowed:

1
2
3
  ifstream fin("numberFile.txt"); // open the file "numberFile.txt" and make fin point to it
nextNum = getNextNum(cin); // nextNum = the next number user types
nextNum = getNextNum(fin); // nextNum = the next number read from the file numberFile.txt

Output stream behavior and structure is analagous to input streams. The parent class for ofstream is ostream, and the cout object is in the ostream class. So the same kind of thing as above may be done with output streams.

Default Values for Parameters

One handy way to make a function like this easier to use is to give its parameter(s) default value(s). A default value for a parameter is a value that is automatically assigned to it if the function is called without a value for that argument. So for example, if we change the header of getNextNum to the following (and keep the function body the same as above):

1
  double getNextNum(istream&amp; instream = cin) // cin is the default value of instream

Then the following two function calls have the exact same effect:

1
2
  nextNum = getNextNum(cin); // specify that number should be read from cin
nextNum = getNextNum(); // use default value (cin) for input stream

cplusplus.com has complete documentation on the iostream libraries