CSCI 1300 - Exercise 5
Input and Output from Files

What You'll Get from This Exercise

Until now, all of the input and output of your programs has been with the keyboard and monitor. You have used cin for the input stream and cout for the output stream. In this exercise, you’ll learn how to use a disk file for input or output.

A Program to Illustrate File Input/Output

You'll start this exercise by getting and running a file demonstration program. You can download copyints.cxx to your working directory. Also download two files named numbers.dat and message.dat, from the same directory. One of these files contains a secret message!

The Purpose of copyints.cxx

  • The copyints program uses techniques from you textbook's chapter on Streams and Basic File I/O. The program does not do a whole lot: merely copy integers from one file to another. In this lab exercise you’ll make a few changes to the program, to ensure that you understand what the program is doing. At the end of the lab exercise, you will make more significant changes to the copyints program, to get it to decode the secret message that is in the numbers.dat file.

Using the copyints Program without Changes

  • Start emacs, and open the file numbers.dat file. You should see a few integers. The copyints.cxx program can read the file numbers.dat, and create a second file of the same numbers. The second file will be named output.dat, and its integers will be printed with just one per line.

    In other words, the copyints program looks for a file named numbers.dat on your disk. If it finds this file, then the file is read, and each is integer written to a second file called output.dat.

    Go ahead open copyints.cxx now. Compile and run the program. On the screen you should receive a message similar to "Input file in_data.cxx copied to output file output.dat; 8 integers copied." At this point, you can open and look at the output file output.dat, which will contain the same integers that are in the in_data file (although output.dat will have only one integer per line).

    Before moving to the next step, you should understand how the copyints program is working. (Re-read Section 8.3 if you have any uncertainties.)

Improving the copyints Program

  • At the moment, the copyints program always uses numbers.dat for its input file, and always uses output.dat for it’s output file. We will change that. To make the change, find these lines in the main program:
    // Names for the text files.
    char in_file[40] = "numbers.dat";
    char out_file[40] = "output.dat";
    
    The data type char, and the number [40] indicates that each of these variables is capable of holding a sequence of up to 40 characters. You’ll read about these sequence of characters later in the semester; for now it is sufficient to know that you can use such a sequence to hold the name of a file such as "numbers.dat" or "output.dat". So, in our program we declare the variables in_file and out_file, and immediately assign the names of the input and output files (numbers.dat and output.dat).

    As an improvement, we will let the program’s user type the name of the input and output files. To carry out the improvement, start by getting rid of the assignments in the four lines that you have just seen. Change the lines to this:

    // Names for the external files,
    // to be read from the user of the program.
    char in_file[40];
    char out_file[40];
    
    Now we have the variables in_file and out_file, but these variables have not been assigned a value. We need to add lines that will read the names of the files from the program’s user. This can be carried out just before each file is opened. For example, the name of the input file is used to open the actual input file in the statment: ins.open(in_file); Just before this open statement we can read the name of the input file, as shown here:
    // Open input and output files, exit on any error
    cout << "Please type the input file name: ";
    cin >> in_file;
    ins.open(in_file);
    
    After these statements, the input file stream (named ins) will be attached to whatever file was requested by the program’s user. For example, if the user types numbers.dat, then the program will read input from numbers.dat. If the user types message.dat, then the program will take input from message.dat.

    We can use the same appraoch to ask the user for the name of the output file. Find the place in the main program where the output file is opened, and add statements to read the name of the output file from the program’s user. Do this now, and then run your program again. When you are prompted for an input file you may type numbers.dat, or type message.dat--the choice is yours. You may type whatever name you like for the output file (though I suggest that the file ends with .dat to indicate that it is a data file rather than a cxx file).

Summary: How to Open a File for Input

  1. Make sure that you have the include directive to include <fstream> and the using namespace std.
  2. Declare two variables: a sequence of characters to hold the name of the input file, and a variable (often called ins) to act as the input file stream. For example:
    char in_file_name[40];
    ifstream ins;
    
  3. Prompt the program’s user for the name of the input file, and read this name. For example:
    cout << "Please type the name of the input file: ";
    cin >> in_file_name;
    
  4. Open the input file, attaching it to the input file stream. Make sure to check that there was no problem opening the file, like this:
    ins.open(in_file_name);
    if (ins.fail( ))
    {
        cerr << "Could not open " << in_file_name << "!" << endl;
        return EXIT_FAILURE;
    }
    
  5. After the input file has been opened, the input file stream ins can be used in exactly the same way that you use the standard input cin. Characters, integers, and other data types can all be read from ins. When the program ends, you should close the input file stream with the statement: ins.close();

Summary: How to Open a File for Output:

  1. Make sure that you have the include directive to include <fstream.h>.
  2. Declare two variables: a sequence of characters to hold the name of the output file, and a variable (often called outs) to act as the output file stream. For example:
    char out_file_name[40];
    ofstream outs;
    
  3. Prompt the program’s user for the name of the output file, and read this name. For example:
    cout << "Please type the name of the output file: ";
    cin >> out_file_name;
    
  4. Open the output file, attaching it to the output file stream. Make sure to check that there was no problem opening the file, like this:
    outs.open(out_file_name);
    if (outs.fail( ))
    {
        cerr << "Could not open " <<< out_file_name << "!" << endl;
        return EXIT_FAILURE;
    }
    
  5. After the output file has been opened, the output file stream outs can be used in exactly the same way that you use the standard output cout. Characters, integers, and other data types can all be written to outs. When the program ends, you should close the output file stream with the statement: outs.close();

Decoding a Secret Message

Modify the program so that instead of writing the numbers to the output file, it writes the characters that each number stands for from the ASCII character code. This code assigns one letter to each number from 0 to 127. For example, the number 65 is assigned the letter A. The C++ expression that you'll need to get the letter from an integer i is:
static_cast<char>(i)
This is an example of a type cast as described in your textbook.

In your program, you should print only these letters (no endls). Run the program with the input file message.dat that you downloaded earlier.

Reading a Whole Line into a String Variable

One other skill is useful. Here's how to read an entire line of a file (called fin) into a string variable called s:

    fin.getline(s);
I often use fin for the name of my ifstream variable and fout for my ofstream variable.