Name: _____________________ Class: Comp 217
SSN/ID:   _____________________ Section & Group: ____________
I/O and Objects


Reading from and writing to a file using Streams for I/O


So far, you have read the input to your programs from the keyboard and have written (displayed) your results on the screen. Input data read from the keyboard and output data written to the screen are temporary and will be lost when the program ends.  In this lab, you will learn to read from/write to a file which will remain on the hard disk until you manually remove them.  The reading from/writing to a file in a program is usually referred to as file I/O (Input/Output).  You will use constructs called streams to read/write your data to or from a file.  Streams are the first examples of objects introduced in this course. An object is simply a special kind of variable. C++ is an Object Oriented Programming (OOP) language, i.e., it has the ability to handle objects.

In order to read from/write to a file, we need to take some precise steps.  Missing any of these steps will result in failure to correctly read from/write to a file.  Following are the steps that you need to go through to successfully perform the file I/O.

"Remember you cannot use a stream of type input to write to a file and vice versa"

Step (1): Include the required directives

#include <fstream>   // This enables you to do file I/O
#include <iosream>   // For cin and cout
#include <cstdlib>   // To use exit(1) that terminates the program
#include <string>    // For string functionality
using namespace std;

Step (2): Create the input/output streams
If fixed file name is used
If file name is an input (file name is not fixed)

 

Create (declare) the streams of type input or output to use them for reading a file or writing to a file.
ifstream inp_stream; // create an input stream type
ofstream out_stream; // create an output stream type

(A)
string inp_file, out_file;
(B)
Create (declare) the streams of type input or output to use them for reading a file or writing to a file.
ifstream inp_stream; // create an input stream type
ofstream out_stream; // create an output stream type

Step (3): Connect the input stream to an input file/the output stream to an output file
At this point you must be most careful. If, by mistake, you connect an out_stream to an input file, you can destroy the input file.
 
If fixed file name is used
If file name is an input (file name is not fixed)

For an input file we have:

inp_stream.open("input_file.txt");   // Assumes a text file named input_file.txt
                                     // exists in the same directory on the disk
if ( inp_stream.fail() ) {
    cout << "Input file opening failed. \n";
    exit(1);   //exit the program
}

Similarly, for an output file we have:

out_stream.open("output_file.txt");
if ( out_stream.fail() ) {
    cout << "Output file opening failed. \n";
    exit(1);   // exit the program
}

For an input file we have:

(A) 

cout << "Input the name of input file\n";
cin >> inp_file;

(B)

  
inp_stream.open(inp_file.c_str());
if ( inp_stream.fail() ) {
    cout << "Input file opening failed. \n";
    exit(1);   //exit the program
}

Similarly, for an output file we have:

  
out_stream.open(out_file.c_str());
if ( out_stream.fail() ){
    cout << "Output file opening failed. \n";
    exit(1);   //exit the program
}

If you decide to append to an existing file, use:
out_stream.open("output_file.txt", ios::app);

Step (4): Use the input stream to read from the input file/the output stream to write the output file

Declare your string variables:
string first_data, second_data;

To read:
inp_stream >> first_data >> second_data;

To write:
out_stream << first_data << second_data;

Step (5): Close the input and output files by disconnecting the corresponding streams
inp_stream.close( );
out_stream.close( );

Note that we have used two different methods to get a file name into the program.  In the method on the left hand side of the table in Steps (2) and (3), we have fixed input and/or output file names.  If the file name is different, the file name within " and " must be changed and the program must be recompiled for the new changes to become effective.  In the method on the right hand side of the table in Steps (2) and (3), we can ask for the file name when we run the program instead of editing and recompiling the program.  This method seems to be more convenient.

The following program reads a file containing some integer values (we do not know how many values).  It displays the number, the number to the power of two, and the sum up to that point. At last it displays the average of these numbers.

// P61.cpp - This program reads some integers from a file and displays:
// The number, number2, and the sum of all numbers read up to that point
#include<iostream>
#include<fstream>  // Step (1)
#include<cstdlib>
#include<cmath>
#include <string> // For string functionality
using namespace std;

int main( )
{
    float x=0.0;
    int count = 0;
    float sum = 0, avg=0;
    string input_file; //Step (2)-A

    ifstream fin;  // Step (2)-B - declaration of the stream of type input

 cout << "Please input the input file name \n"; // Step (3)-A Get the file name
 cin >> input_file;

    fin.open(input_file.c_str()); // Step (3)-B - connect to the input file and test
    if(fin.fail( ))
    {
       cout << "Input file opening failed. \n";
       exit(1);  // if we couldn't open the file to read from we exit
    }

    cout << "\t  x \t\t x^2 \t\t Current Sum \n";
    cout << "\t === \t\t === \t\t ========== \n";

    while( fin >> x) // Step (4)-Read all numbers one-by-one to the end of the file
    {
         sum = sum + x;
         cout << "\t  " << x <<"\t\t  " << pow(x,2) << "\t\t  " << sum << "\n";
         count++;
    }

 avg = sum/count;
 cout << "\n \t\t The average of these " << count << " numbers is: " << avg << endl;

    fin.close( );  // Step (5)-Close the connection (close the file)

   return 0;
}

Every input or output file seems to have two names.  The actual names, or external file, is the name of the file as it appears on your computer disk.  The name of the stream used to read from/write to a file is referred to as internal file name.  The internal name is actually the name of the stream that you are using to read from/write to the input/output file.

Introduction to Classes and Objects
In C++ the streams cin, cout, and any other streams that you may declare are called objects.  You use the class ifstream to define objects of type ifstream and class ofstream to declare objects of type ofstream.  An object is a new type of variable that allows you to include functions, as well as data, in it.  An object may include several variables and functions of various types. In the above program, we have an object called fin.  This object has several functions that were used in that program.  These functions are open(....), close( ), and fail( ).  The functions are called member functions.  Note that you would have similar functions for a stream of type output, but they may not have the same functionality as they are members of a different object type.  As you can see in the above program, the member functions are called using the dot operator (ex. fin.close( ) ). The period between the fin (calling object) and the close( ) (member function) is referred to as the dot operator.

Exercise 6.1
Carefully copy or cut and paste the program P61.cpp into a file called ex61.cpp.  Compile and run the program to make sure it compiles without any error.  Create a file, input.txt, and type the following 8 integers in it: 4 4 4 4 4 4 4 4.  Save the file and exit.  Now, run your program and when the program asks for the input file, type input.txt.  Your program should display an output similar to the one given below:

Please input the input file name.
input.txt
          x               x^2             Current Sum
          ===             ===             ==========
          4               16              4
          4               16              8
          4               16              12
          4               16              16
          4               16              20
          4               16              24
          4               16              28
          4               16              32

                 The average of these 8 numbers is: 4

This is the correct result.

Exercise 6.2
Modify the above program such that the program writes to the output to a file called output.txt, instead of displaying it.

Streams as Arguments to Functions


A stream can be the argument to a function.  The only restriction is that any input or output must be passed to a function must be passed as call-by-reference.  Here is a modified version of P61.cpp in which we have used a function that reads a file name and then returns a stream, fin, that is connected to that file to the main function.

// P62.cpp - This program reads some integers from a file and displays:
// The number, number2, and the sum of all numbers read up to that point
#include<iostream>
#include<fstream>  // Step (1)
#include<cstdlib>
#include<cmath>
#include <string> // For string functionality
using namespace std;

void get_stream(ifstream & fin);

int main( )
{
    int count = 0;
    double x = 0.0;
    float sum = 0.0, avg;

    ifstream fin;  // Step (2)-B - declaration of the stream of type input

    get_stream(fin);

    cout << "\t  x \t\t x^2 \t\t Current Sum \n";
    cout << "\t === \t\t === \t\t ========== \n";

    while( fin >> x) // Step (4)-Read all numbers one-by-one to the end of the file
    {
         sum = sum + x;
         cout << "\t  " << x <<"\t\t  " << pow(x,2) << "\t\t  " << sum << "\n";
         count++;
    }

 avg = sum/count;
 cout << "\n \t\t The average of these " << count << " numbers is: " << avg << endl;

    fin.close( );  // Step (5)-Close the connection (close the file)

   return 0;
}

 void get_stream(ifstream &fin)
{
    string input_file; //Step (2)-A
    cout << "Please input the input file name \n"; // Step (3)-A Get the file name
    cin >> input_file;

    fin.open(input_file.c_str()); // Step (3)-B - connect to the input file and test
    if(fin.fail( ))
    {
       cout << "Input file opening failed. \n";
       exit(1);  // if we couldn't open the file to read from we exit
    }
}

Exercise 6.3
Modify the above program such that the program writes to the output file output.txt.  You need to modify the function get_stream such that it returns two streams to the main, one for the input and the other for the output.  Call your new program ex63.cpp.

Reading to The End of File


In Program P62.cpp, you read all the integers from a file and did the required computations.  In that program, you used:

    while( fin >> x) { // Step (4)-Read all numbers one-by-one to the end of the file
         ....

    }

to do the reading.  Let's see if we can use a similar technique to read a text file and display its contents.  In order to do this, let's first create the following input file.

Exercise 6.4
Create a file called act64.txt and type the following text exactly as it appears below into that file. You may cut and paste the following 10 lines into that file.

We have learned that "\n" means go to a new line
when it is used with the cout statement.
In C++, '\n' and "\n" look very similar.
In fact, in a cout statement, they both act the same.

However, they do not mean the same in all situations.
'\n' is a value of type char and can be stored in a variable
of type char, for example:  int c = '\n';
On the other hand, "\n" is a string,
that happens to be made up of one character.

The following program uses a similar technique as in program P62.cpp to read the entire contents of the act64.txt file and to display its entire contents exactly as they appear above.

// P64.cpp - This program reads the entire contents of an input file and will
// display it with the same format.
#include<iostream>
#include<fstream>  // Step (1)
#include<cstdlib>
#include <string> // For string functionality
using namespace std;

void get_input_stream(ifstream & fin);

int main( )
{
    char c;
    ifstream fin;  // Step (2)-B - declaration of the stream of type input

    get_input_stream(fin);

    cout << "Here are the entire contents of the input file \n";

    while( fin >> c) // Step (4)-Read all numbers one-by-one to the end of the file.
    {
         cout <<  c;
    }

    cout << "\n I am done with writing the contents of the file \n";

    fin.close( );  // Step (5)-Close the connection (close the file)

    return 0;
}

void get_input_stream(ifstream & fin)
{
 string input_file; // Step (2)-A
    cout << "Please input the input file name \n"; // Step (3)-A Get the file name
    cin >> input_file;

    fin.open(input_file.c_str()); // Step (3)-B - Connect to the input file and test
    if(fin.fail( ))
    {
       cout << "Input file opening failed. \n";
       exit(1);  // if we couldn't open the file to read from we exit
    }
}

Exercise 6.5
Cut and paste or carefully copy the P64.cpp program into a file called ex65.cpp.  Compile the program to make sure it compile without any error.  Then run the program and use the act64.txt file as the input file.

Does this program produce the same exact output as shown above?

What do you think the problem is?

The get and put Member Functions

We will help you find the answer very soon.  The problem is that cin does not read the white spaces, i.e., it skips blank spaces, tabs (\t), and new lines (\n).  Thus, the entire text will appear in one piece without the separating spaces and new lines.  In order to read and write the entire text with correct spacing, we will use a member function with the input stream.  The get(c) function, where c is a character, allows us to read all characters from the file one character at a time.  So to fix the above program we could simply us this function instead of the cin.  We used:

   while ( fin >> c) ( // Step (4)-Read all numbers one-by-one to the end of the file
       ....
   }

to read the contents of the file.  Now we can replace the fin >> c with fin.get(c).

Exercise 6.6
Modify the ex65.cpp program by replacing fin >> c with fin.get(c) in the while loop.  Compile, then run the program for the act63.txt input.  Call your new program ex66.cpp.

Does your program produce the correct output this time?

The member function put(c) will do the opposite of what the  member function get(c) does. It writes to the output stream one character at a time.   To practice with this function, in program ex66.cpp, you can replace the:
         cout << c;

with
         cout.put(c);

That does the same thing.

The eof Member Function
There is yet another way to read a file (any file) to the end.  The member function eof( ) can be used with a stream of input type to determine the End-Of-File.  This function returns true when the end of the input file is reached.  Thus, it can be used in a while loop to control the looping process.  In general, you need to read one character before you check to see if the end of the file is reached.  Here is a way to use the eof( ).  Please note that we haven't shown the complete program.

// Other lines of program
ifstream fin;
char next_char;
// Other lines of programs

fin.get(next_char);

while( ! fin.eof( ) ) {
     cout << next_char;       // you could use cout.put(next_char) too
     fin.get(next_char);
}

Exercise 6.7
Modify the program ex65.cpp by using the eof( ) to read to the end of file act63.txt and to display its entire contents.  Make sure to check that the displayed contents are exactly the same and in the same format.

Predefined Character Functions in cctype (ctype)


In C++, we have several predefined character functions.  These functions are listed below:
 
 

Function Name
Description
Example
toupper(a_character) Returns the uppercase version of the argument character.  If the character is already an uppercase, it will remain unchanged. char c, d = 'a';
c = toupper(d);
cout << c;
Output is:  A
tolower(a_character) Returns the lowercase version of the argument character.  If the character is already a lowercase, it will remain unchanged. char c, d = 'A';
c = tolower(d);
cout << c;
Output is:  a
isupper(a_character) Returns true if the character is an uppercase letter, otherwise it returns false char d = 'A';
cout << isupper(d);
Output is:  1
Note: it can be used in an if statement
islower(a_character) Returns true if the character is a lowercase letter, otherwise it returns false char d = 'A';
cout << islower(d);
Output is:  0
Note: it can be used in an if statement
isalpha(a_character) Returns true if the character is one of the letters of the alphabet, otherwise it returns false char d = 'A';
cout << isalpha(d);
Output is:  1
Note: it can be used in an if statement
isdigit(a_character) Returns true if the character is one of the digits '0' to '9', otherwise it returns false char d = 'A';
cout << isdigit(d);
Output is:  0
Note: it can be used in an if statement
isspace(a_character) Returns true if the character is a whitespace character, blank space, newline, or tab, otherwise it returns false char d = '\n';
cout << isspace(d);
Output is:  1
Note: it can be used in an if statement

The following program reads one character from the keyboard and will display the character in uppercase if it is lowercase and does the opposite when the character is in uppercase.  If the character is a digit, it displays a message with the digit.

// P64.cpp - This program reads one character from the keyboard and will
// convert it to uppercase, if it is lowercase
// convert it to lowercase, if it is uppercase
// display a message, if it is a digit

#include<iostream>
#include<cctype>
#include<string>
using namespace std;

int main( )
{
    char c;

    cout << "Enter a character \n";
    cin >> c;

     if(isalpha(c))
    { //check to see if it is a letter of alphabet
        if( isupper(c) ) //check to see if it is uppercase
        {
            c = tolower(c);
            cout << "Your character " << c << " is in uppercase.";
            cout << "Its lowercase case is " << c << endl;
        }
        else
        {
            c = toupper(c);
            cout << "Your character " << c << " is in lowercase.";
            cout << "Its uppercase is " << c << endl;
        }
     }
     else
     {
            cout << "Your character " << c << " is a digit.\n";
     }

     return 0;
}

Exercise 6.8
Modify the above program such that if one of the whitespaces is entered, it displays a message and tells what the character was (Note: Please recall what the extraction operator does with whitespace). Call your new program ex68.cpp.
 


Some Advanced File I/O: If you'd like to be able to do some advanced File I/O (like repositioning the marker, seeking certain spots, reading/writing binary files, etc.), please see here.

Hand In: This lab handout with the answers filled in attached to a listing of your final programs
(use the enscript command from your Programming Style Sheet to print it out:
enscript -E -G -2rj -M Letter -PECT2_PS <filename> or a2ps <filename>).


Ricky J. Sethi <rickys at sethi.org>
Last modified: Tue Jun 13 16:36:59 PDT 2006