Welcome to Lesson 20!


Learning Objectives

By the end of today's class, you should know...
  • How do you read input from a file?
  • How do you write output to a file?
  • What is the difference between pass by reference and pass by value?
  • How do you indicate a reference parameter?

Announcements

  • Quiz 7 after the break
  • Don't forget Lab 10 due Friday!

Review Activity

With a partner, write and call the following functions:
Function 1
  • The function is named printGrade
  • It takes in a double for the score
  • It prints out the grade to the console as A, B, C, D, or F with a message, "The grade is: <grade>"
  • It returns nothing

Function 2

  • The function is named convertGrade
  • It takes in a double for the score
  • It determines if the grade is A, B, C, D, or F
  • It returns a char for the grade

Now, using the starter code below, call the above printGrade and convertGrade functions:

#include <iostream>
using namespace std;

int main() {
    double score;
    char grade;
    cout << "Please enter your score: ";
    cin >> score;
   
    //call printGrade here
    //Note: when calling printGrade, I can only display the grade to the console

    //call convertGrade here
    cout << "Your grade is " << grade << endl;
    //Note: when calling convertGrade, I can store the grade it returns
    //and do more with it than display it. It is
    //stored for future use.

}

Value Parameters

  • There are two ways to pass arguments to parameters
  • All our functions so far have used value parameters
  • Value parameters are separate variables from the ones in main() (or another calling function)
  • Modification of value parameters does not affect the original caller's value when the function returns

How Value Parameters Work

  • During the function call, your program copies the argument's value into the parameter variable
  • The scope of value parameters is the same as the scope for a local variable
  • If the parameter is assigned a new value, only the local copy changes
  • When the function returns, your program discards any value assigned to a parameter variable
  • The following example program uses value parameters
  • What does this program output?

Example of Value Parameters

#include <iostream>
using namespace std;
int mystery(int param);
int main() { int num = 2; cout << "At first, num=" << num << endl; int result = mystery(num); cout << "After calling, num=" << num << endl; cout << "And result=" << result << endl; return 0; }

int mystery(int param) {
    cout << "param=" << param << endl;
    param = param * 2;
    return param;
}


Reference Parameters

  • C++ has another parameter-passing mechanism known as call-by-reference
  • A reference parameter does not create a new variable, but refers to an existing variable instead
  • Any change in a reference parameter is actually a change in the variable to which it refers
  • We create a reference parameter by using an ampersand (&) between the parameter's type and name
    parameterType& parameterName
    
  • The following program shows an example of reference parameters
  • What is different?
  • What does this program output?

Example of Reference Parameters

#include <iostream>
using namespace std;

int mystery(int& param);
int main() { int num = 2; cout << "At first, num=" << num << endl; int result = mystery(num); cout << "After calling, num=" << num << endl; cout << "And result=" << result << endl; return 0; }
int mystery(int& param) {
    cout << "param=" << param << endl;
    param = param * 2;
    return param;
}
 

Call-By-Reference Details

  • What's really passed to the reference parameter?
  • A reference to the caller's original argument!
  • Essentially a reference parameter is another name for the original argument
  • With a reference parameter the code uses the original argument variable in calculations
  • Any change made to a reference parameter changes the original argument
  • Because C++ is passing variables, arguments for reference parameters must be variables and not constants



Mixed Parameter Lists

  • Parameter lists can include both value and reference parameters
  • As usual, the order of arguments in the list is critical
  • The following is a function signature with mixed parameter types:
    void mixedCall(int& par1, int par2, double& par3)
    
  • To call the function:
    int arg1 = 0, arg2 = 1;
    double arg3 = 2.2;
    mixedCall(arg1, arg2, arg3);
    
  • arg1 must be an integer type and is passed by reference
  • arg2 must be an integer type and is passed by value
  • arg3 must be a double type and is passed by reference

When to Use Reference Parameters

  • Reference parameters are usually more efficient than value parameters because they do not make copies of the parameters:
    • A program simply passes the memory address to the function
    • No new memory space is allocated and deallocated
  • Therefore, function calls using reference parameters usually operate faster
  • However, reference parameters restrict the arguments we can use for a function
  • Specifically, we must use a variable argument and not a literal or constant value
  • Usually, the best practice is to pass an object, like a String, by reference
  • Also, we should use value parameters unless a function needs to modify a parameter
  • The performance advantage of reference parameters for primitive types tends to be negligible
  • Thus, it is not worth restricting the call pattern of a function

Reference vs Value Video


Activity 20.1: Exploring Call by Reference (10 pts)

In this exercise we explore how call-by-reference parameters differ from call-by-value parameters.

Specifications

  1. Copy the following program into Eclipse and save it as swap.cpp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    #include <iostream>
    using namespace std;
    
    void swap(int var1, int var2);
    
    int main() {
        int num1 = 0, num2 = 0;
        cout << "Enter two integers: ";
        cin >> num1 >> num2;
    
        swap(num1, num2);
    
        cout << "After calling function:  "
             << num1 << " " << num2 << endl;
        return 0;
    }
    
    void swap(int var1, int var2) {
        int temp = var1;
        var1 = var2;
        var2 = temp;
    }
    
  2. Compile and run the starter program to make sure you entered it correctly. When you run the program, the output should look like this:
    Enter two integers: 1 2
    After calling function: 1 2
    

    Notice that num1 and num2 have the same values before and after calling the function swap(). Any value assigned to var1 and var2 have no effect on num1 and num2.

  3. Change your program by adding the four ampersands (&) circled below:

    The ampersands tell C++ to use call-by-reference when passing parameter values.

  4. Compile and run the modified program to make sure you made the changes correctly. When you run the program, the output should look like this:
    Enter two integers: 1 2
    After calling function: 2 1
    

    Notice that num1 and num2 have different values before and after calling the function swap(). Any value assigned to var1 and var2 change num1 and num2 respectively.

  5. Submit your final program source code to Canvas.


Program I/O

  • Program I/O = Program Input/Output
    • Input to and output from programs
  • Input can be from a keyboard, mouse or file
  • Output can be to a display screen, printer or file
  • Note that files can be both input and output devices for programs
  • Advantages of file I/O:
    • Data still exists after the program ends
    • Input can be automated (rather than entered manually)
    • Output from one program can be input to another
  • To store and retrieve data in a file, we need two constructs:
    • A file
    • A file stream object
  • We will look at files first

Files

File: a collection of data stored under a common name on a storage medium.

  • Files provide long-term storage of large amounts of data
  • Usually, you store files on durable storage mediums
    • Magnetic disks
    • Optical disks
    • Flash storage, like USB storage devices
  • Files are a single sequence of bytes

    Byte 0 Byte 1 Byte 2 ... Byte n−1 End-of-file marker

  • Since the data arranged as bytes, any type of data can be stored in a file
  • The operating system keeps track of the number of bytes in a file
  • Files must have a name
    • Naming requirements depend on the underlying operating system (OS)
  • The operating system organizes files into directories

Types of Files

  • All data in a file is ultimately just zeros and ones
  • Each binary digit can have one of two values: 0 or 1
  • A bit is one binary digit
  • A byte is a group of eight bits
  • These binary digits may represent integer values or text characters
  • It is up to the program using the file to understand the meaning and internal format of the data
  • In general, programs interpret data using two broad categories: text and binary

Text Files

  • In text files, the bits represent printable characters
  • Files are usually stored as one byte per character (ASCII)
  • Each line is delimited by end-of-line characters:
    • Macintosh (before OS-X): "\r"
    • Unix: "\n"
    • Windows: "\r\n"
  • An example of a text file is source code
  • We can read text files because each byte is interpreted by a program as textual characters
  • Some of these programs, like TextPad, then display the textual data to your computer's screen
  • Since there are many programs that read and display text, text files are called human readable

Binary Files

  • Data other than text is usually referred to as binary data
  • Each bit represents some type of encoded information
    • Such as program instructions or integer data
  • Binary files are easily read by the computer but not by humans
  • The following table compares binary and text values saved in a file
  • First we consider the value "1234" as ASCII codes and compare these bits to a binary value of 1234
  • As we can see, the bit patterns are different for the same data when stored as text or binary

Comparing Binary and Textual Data

Description Byte 0 Byte 1 Byte 2 Byte 3
"1234" as char's '1' '2' '3' '4'
"1234" as ASCII codes (bytes) 49 50 51 52
"1234" as ASCII codes (bits) 00110001 00110010 00110011 00110100
(int) 1234 as binary bits 00000000 00000000 00000100 11010010

Streams

Stream: a one-way transmission path that either delivers data to a destination (screen, file, etc.) or that takes data from a source (keyboard, file, etc.)

  • A stream connects a program to an I/O object
  • Input stream: an object that provides a sequence of bytes to a program
image depicting an input stream

  • Output stream: an object that accepts a sequence of bytes from a program
image depicting an output stream

  • cin and cout are input and output streams


File Streams

File stream: a one-way transmission path used to connect a program to a file.

  • File streams can be either input or output streams
  • File input streams receive data from a file
  • File output streams send data to a file
  • File I/O uses streams of type ifstream and ofstream
  • To declare a file input stream, we write code like:
    ifstream fin; //declares a file input stream
  • Similarly, to declare a file input stream we write code like:
    ofstream fout; //declares a file output stream
  • Each file your program uses will need a separate file stream object

Streams and Objects

  • Streams are objects and thus cin and cout are objects
  • Objects are special variables that can have a function associated with them
  • To call a function of an object, we use the dot operator
  • An example of using the dot operator is with cin is shown below
cin.fail();

Example of File I/O

  • Let us consider an example that reads from a file and writes to a file
  • The program reads from a file named infile.txt, which contains the following values:
    10
    20
    30
    
  • After summing the values, the program writes them to a file named outfile.txt
  • Consider the following code and try to identify:
    1. What is the name of the input stream?
    2. Which line opens a file for reading
    3. What is the name of the output stream?
    4. Which line opens a file for writing
    5. Which line reads data from the input stream?
    6. Which lines write data to the output stream?

Example Program to Read and Write Files



//Reads three numbers from the file infile.txt,
//sums the numbers, and writes the sum to the
//file outfile.txt.

#include <fstream>   // for file I/O
#include <iostream>
#include <cstdlib>
using namespace std;

int main() {
    ifstream fin;
    fin.open("infile.txt");
    if (fin.fail()) {
        cout << "Input file opening failed.\n";
        exit(-1);
    }

    ofstream fout;
    fout.open("outfile.txt");
    if (fout.fail()) {
        cout << "Output file opening failed.\n";
        exit(-1);
    }

    int first, second, third;
    fin >> first >> second >> third;
    fout << "The sum of the first 3\n"
         << "numbers in infile.txt\n"
         << "is " << (first + second + third)
         << endl;

    fin.close();
    fout.close();

    cout << "Processing completed\n";

    return 0;
}

Closing a Stream

  • After finishing reading and writing you should close the file streams
  • If you do not close an output stream, you may lose data stored in the output buffer
  • In addition, streams consume system resources and you should not keep open any more streams than needed

Procedure For File I/O

  1. Place the following include directives in your program file:
    #include <fstream>   // for file I/O
    #include <iostream>  // for cout
    #include <cstdlib>   // for exit()
    using namespace std;
    
  2. Declare names for input and output streams like:
    ifstream fin;
    ofstream fout;
    
  3. Connect each stream to a file using open() and check for failure:
    fin.open("infile.txt");
    if (fin.fail()) {
        cout << "Input file failed to open.\n"; exit(-1);
    }
    
    fout.open("outfile.txt");
    if (fout.fail()) {
        cout << "Output file failed to open.\n";
        exit(-1);
    }
    
  4. If there is a failure, we need to check that the input file is stored in the same directory that the .cpp file is executing.
  5. Read or write the data:
    • Read from a file with fin like using cin:
      fin >> first >> second >> third;
      
    • Write to a file with fout like using cout:
      fout << "first = " << first << endl;
      
  6. Close the streams when finished reading and writing:
    fin.close();
    fout.close();
    

More Information


How to Save Your Files Using Eclipse

  • If you are using Eclipse for your assignments in this class, you can simply save the file in the same project folder as your program (not under src)
  • Right click on the project folder in the project explorer (right side bar)
  • Then, go to New->File from the drop down menu that appears
  • Name your file using the extension .txt
  • Note: if your project explorer is hidden, go to the following: Window->Show View->Other...->General->Project Explorer
     

Activity 20.2: Two Numbers (10 pts)

  • In this exercise we write a program that copies two numbers from an input stream to an output stream.
  • Copy the following program into a C++ file, save it as copytwo.cpp, and then compile and run the starter program to make sure you copied it correctly.
    #include <iostream>
    using namespace std;
    
    int main() {
        // Enter your code here
    
        return 0;
    }
    
  • Then, let's create a text file to test our program. Open up a new file in Eclipse by right-clicking the name of the project and selecting New->File.
  • Name your file infile.txt.
10
20

  • Stop!! Did you save the file under the src folder? This is not the correct location. Drag and drop it into your main project folder.
  • We will read from this file after writing our program.

  • Place the following include directives in your source code file:
    #include <iostream>  // for cout
    #include <fstream>   // for file I/O
    #include <cstdlib>   // for exit()
    using namespace std;
    
  • Inside main(), declare names for the input and output streams:
    ifstream fin;
    ofstream fout;
    
  • Add code to connect each stream to a file using open() and check for failure:
    fin.open("infile.txt");
    if (fin.fail()) {
        cout << "Input file failed to open.\n";
        exit(-1);
    }
    
    fout.open("outfile.txt");
    if (fout.fail()) {
        cout << "Output file failed to open.\n";
        exit(-1);
    }
    
  • Add statements to read two numbers from the input stream. For example, here is possible code for reading the first number:
    int first;
    fin >> first;
    
  • Add statements to write the two numbers to the output stream. For example, here is possible code for writing the first number:
    fout << "first = " << first << endl;
    
  • Close the streams when finished reading and writing:
    fin.close();
    fout.close();
    
  • Compile and run your modified program to make sure you made the changes correctly.

    Notice that you do not see any output on the screen for file reading or writing. The output stream wrote the program output to the output file.

  • Save your program source code file to submit to Canvas

Listing of copytwo.cpp

copy two program

Using Loops to Read Files

  • Sometimes we do not know how many data items are in a file
  • To solve this problem, the typical approach is to use a loop to process the file
  • When reading input from the console, we can read the input as part of the test condition like:
    while (fin >> nextNum) { //returns true or false AND reads in the number!
        // process the input
    }
    
  • We saw this technique when we were checking cin for failure
  • While the read is successful, the input stream returns a value interpreted as true
  • If the stream fails or closes, then the test condition fails
  • This failing condition returns a value interpreted as false by the loop
  • Thus the loop ends and the program continues after the loop statement
  • We can use this behavior to read from a file as shown below

Example Program Reading a File Using a Loop


#include <fstream>   // for file I/O
#include <iostream>
#include <cstdlib>
using namespace std;

int main() {
    ifstream fin;
    fin.open("infile.txt");
    if (fin.fail()) {
        cout << "Input file failed to open.\n";
        exit(-1);
    }

    double nextNum, sum = 0;
    int count = 0;
    while (fin >> nextNum) {
        cout << "Read: " << nextNum << endl;
        sum = sum + nextNum;
        count++;
    }
    cout << "average = " << (sum / count) << endl;
    fin.close();

    return 0;
}



Activity 20.3: Averages (10pts)

  • We are going to write a program that takes in a list of numbers of unknown size from a file, and then outputs the average of these numbers.
  • This activity will help you practice using a loop to help you read data from a file.
  • Open up a new C++ file in Eclipse and name it averages.cpp
  • Copy and paste the starter code below into your file.
#include <iostream>
using namespace std;

int main() {
    
    return 0;
}
  • Now add the two additional libraries that are required for file I/O to the top of your program:
#include <fstream>
#include <cstdlib>
  • Next, lets create a new input stream variable at the top of main
ifstream fin;
  • Then, let's use this stream to open a file called numbers.txt
fin.open("numbers.txt");
  • Next, we will need to make sure that the file opened successfully by checking for failure:
if (fin.fail()) {
        cout << "Input file failed to open.\n";
        exit(-1);
}

  • Next, lets read in the numbers from the file. Since we are computing the average of the numbers, we need two pieces of information
1. The sum of the numbers
2. How many numbers there are
  • Remember: Average = sum / count
  • Therefore, as we read in the numbers from the file, we need to computer their sum and count how many there are.
  • We will therefore need two variables to keep track of this information. Add the following two variable declarations to the top of main:
double sum = 0.0;
int count = 0;
  • We will also need a variable to temporarily store each number as we read it in from the file. Add an additional variable declaration to the top of main like so:
double num;
  • Now, let's read in the numbers from the file and process them inside a loop. Add the following loop to your program:
while (fin >> num) {
    cout << "Processing the number: " << num << endl;
    sum += num; //adding the number to our running total for the sum
    count++; //counting how many numbers are in the file
}
  • Now, let's print the average to the console and output it to a file.
  • To print the average to the console, add the following line of code:
cout << "The average is: " << sum/count << endl;
  • To output the average to a file, we need to open a new output stream. Below the cout statement, add the following line of code:
ofstream fout;
  • Then, we need to open a file to write out to it. Let's open a file named average.txt.
fout.open("average.txt");

  • Before we can proceed, we need to make sure the file opened properly:
if (fout.fail()) {
    cout << "Output file failed to open." << endl;
    exit(-1);
}

  • Finally, lets write the average to the file.
fout  << "The average is: " << sum/count << endl;
  • As a last step we need to close our input and output streams. Add the following lines of code to the bottom of main:
fin.close();
fout.close();
  • Let's create a file to test our program. Create a new text file in Eclipse called numbers.txt with the following contents:
10
20
30
40
50
  • Save the file in the same directory in which you are executing your source code files and close it.
  • Now, run the code and open up your file to make sure everything is working properly. Open up the file averages.txt and verify that you got the correct output.
  • When you are finished, upload averages.cpp to Canvas.
}

Reading Files using getline()

  • Recall that you can read text using a loop and an input stream like fin:
    int count = 0;
    string word;
    while (fin >> word) {
        count++;
    }
    cout << count << " words.\n";
    
  • However, just like with cin, there are complications when you want to read words with spaces between them
  • >> skips whitespace and stops on encountering more whitespace
  • Thus, you only get a single word for each input variable
  • If you want to read a complete line of text like "Hello Mom!", you need to use getline()
  • For example:
    ifstream fin;
    // ... more code here
    string line;
    getline(fin, line);
    cout << "Read data: " << line << endl;
    
  • Recall that getline() stops reading when it encounters a '\n'
  • By contrast, fin >> variable operates as follows:
    1. Skips whitespace
    2. Reads characters
    3. Stops reading when whitespace is found
  • Thus if you mix fin >> variable followed by getline(fin, line), you get mysterious results
  • Just like with cin, you get around this problem you by either:
    1. Only use getline() before using fin (and never after using fin)
    2. Use fin >> ws; before using getline()

Using Loops with getline()

  • Sometimes you need to read and process an unknown number of lines in a file
  • For this you can use the getline() function in a loop as well:
    while (getline(fin, line)) {
        // process input
    }
    
  • While the read is successful, the getline() function returns a value interpreted as true
  • If the stream fails or closes, then the test condition fails
  • You can see an example of using getline() to read lines from a file in the following program

Example Program Reading a File Using getline() in a Loop


#include <fstream>   // for file I/O
#include <iostream>
#include <cstdlib>
using namespace std;

int main() {
    ifstream fin;
    fin.open("infile.txt");
    if (fin.fail()) {
        cout << "Input file failed to open.\n";
        exit(-1);
    }

    string line;
    int count = 1;
    while(getline(fin, line)) {
        cout << "Line " << count << ": "
             << line << endl;
        count++;
    }

    fin.close();

    return 0;
}


Activity 20.4: Sonnet Statistics (10 pts)

  • Open up a new C++ file named statistics.cpp and add the starter code below to it:

#include <iostream>

//Add library for file I/O

//Add library for exit()

using namespace std;

int main()

{

    int count = 0;

    string word, line;

    return 0;

}

  • Now, create a new text file in Eclipse called sonnet.txt and copy and paste the below sonnet into your file:

Shall I compare thee to a summer’s day?
Thou art more lovely and more temperate:
Rough winds do shake the darling buds of May,
And summer’s lease hath all too short a date;
Sometime too hot the eye of heaven shines,
And often is his gold complexion dimm'd;
And every fair from fair sometime declines,
By chance or nature’s changing course untrimm'd;
But thy eternal summer shall not fade,
Nor lose possession of that fair thou ow’st;
Nor shall death brag thou wander’st in his shade,
When in eternal lines to time thou grow’st:
So long as men can breathe or eyes can see,
So long lives this, and this gives life to thee.

  • We are going to write some code to count the number of words and the number of lines in this file.
  • First, declare a new input stream variable named fin.

ifstream fin;

  • Next, open up sonnet.txt for reading.

fin.open("sonnet.txt");

  • Don't forget to check for failure!

if(fin.fail())
{
    cout << "Input file failed to open!" << endl;
    exit(-1);
}
  • Note that I have provided you with the code above, but you will need to know it from your own memory for the final!
  • You can practice by remembering the code for the output stream.
  • Beneath your if statement to check for input file failure, create a new output stream variable named fout.
  • Then, open up a new text file named statistics.txt for writing.
  • Don't forget to check for failure!
  • Now, let's write a while loop to count up how many words are in the file.
  • Will you need getline(fin, line) or will you need fin >> word?
while (?????) {
    count++;
}
  • Finally, write an fout statement to print the number of words contained in the file:
fout << "The sonnet has " << count << " words\n";
  • Now, close your input file stream.
fin.close();

  • Now, we need to count the number of lines in the file.
  • For this purpose, we are going to need a new input file stream as we already used the previous one to count the number of words in the file (and we cannot reset the input stream to point to the beginning of the file).
  • Add the following code to your program below the statement to close fin:
ifstream fin2;
fin2.open("sonnet.txt");
//add code to check for failure here
  • Next, we are going to write a while loop to count the number of lines in the file.
  • Do we need getline(fin2, line) or fin2 >> line as the test condition of our while loop?

count = 0; //reset count variable to 0
while (?????) {
    count++;
}
  • Write an fout statement to print out the number of lines in the sonnet.
The sonnet has 14 lines.
  • Finally, close fin2 and fout and run your program.
  • Note that the sonnet has 114 words and 14 lines.
  • Did you get the expected result inside of statistics.txt?
  • Submit your code to Canvas when you are finished.

Wrap Up

  • With a partner, answer the questions from today's learning objectives


Upcoming Assignments

  • Lab 10 due Friday
  • Assignment 20 due Tuesday

}