12. Objects and classes 2, I/O and streams

Review Topics


General Information

Housekeeping

  • Make sure you follow along with this page linked in Canvas
  • Please keep your microphone off unless you are asking a question
  • Please turn on camera if you can (optional)
  • Use chat if you would like to comment or ask questions

Announcements

See Announcements link in Canvas for course information. Here are some other reminders:

  • Complete vaccinations required to visit campus or sign up for future terms (instructions)
  • Remember that PAs and the Individual Readiness Assurance Quiz are due before class on Tuesday.
  • Remember to post in the Pair programming partners discussion group if you need a partner
  • Remember that CAs and the exercises from this page are due Sunday at 9:00pm.
  • Remember that CAs, Labs, and Class Exercises may be completed up to two days late but with a 10%/day penalty.
  • Remember that some lab solutions are posted in Canvas Modules
  • Food & Housing Resources: free food, meals, temporary and permanent housing
  • COVID-19 Resources and Information: Includes loaner-laptop information
  • Campus WiFi Access
    • Aptos: parking lots K and L on this Cabrillo Aptos Map
    • Watsonville: parking lot at Watsonville Center
  • Extra credit bonus points for the first student to report a problem

Homework Help

12.1: Readiness Assessment Quizzes

  • Reading and participation activities are due before the first class meeting of the week
  • Quizzes assess the comprehension of the reading and participation activities

Quiz Part 1: Individual Readiness Assessment

  • Complete this quiz solo to assess your reading comprehension and readiness
  • Must take this quiz before the first class meeting of the week to ensure you are ready for the team quiz
  • Cannot take the quiz late
  • Quiz is open book and notes but timed
  • Highest score is counted so take the quiz multiple times

Quiz Part 2: Team Readiness Assessment (20m)

  • Must attend the class meeting to take this quiz
  • Login to Canvas and enter the access code
  • Will move to breakout rooms with your team
  • Make sure you have the access code for the exam
  • Openly discuss what you believe to be the best answers for the questions
  • Decide how to agree on the answers
    • Strive to reach a consensus on quiz answers
    • If no consensus, work it out as you and others in your group see fit
  • Turn in the quiz as a group
  • Each group member will receive the same score
  • Return to the main meeting room when finished

Quiz Appeals

  • After completing the team quiz, team members may appeal an answer
  • Appeals can be based on two criteria:
    1. Question is factually wrong

      Appeal must included citations to sources of information that document or support an alternative answer. Team may access reference materials during the appeal.

    2. Question is confusing based on it's wording

      Appeal must include an appropriate rewrite of questions or answers that you interpret as ambiguous or confusing.

  • Work with teammates to develop and write any appeals
  • Team has up to 24 hours after the quiz to email appeal to instructor
  • If appeal is granted, only the teams that submitted appeal gets credit

12.2: Streams and Formatting

Programs need a way to input and output data. In C++ input and output are provided with streams. A stream is an object that handles input or output. We have been using streams since the first week.

12.2.1: Reviewing Streams and Formatting

  • Programs need a way to output data to a screen, file, or elsewhere
  • Output stream: an object that accepts a sequence of bytes from a program and delivers them to a source

    output stream

  • Input stream: an object that provides a sequence of bytes to a program from a source

    input stream

  • cout and cin are output and input streams respectively

Output Streams

  • ostream, short for "output stream," is a class that supports output
  • To use must #include <iostream> in namespace "std"
  • An ostream provides the << operator (insertion operator)
  • The insertion operator converts different types of data into a sequence of characters
  • cout is a predefined ostream object pre-associated with a system's standard output, usually a computer screen.

Input Streams

  • istream, short for "input stream," is a class that supports input
  • An istream provides the >> operator (extraction operator)
  • The extraction operator gets data from a data buffer and writes the data into different types of variables
  • cin is a predefined istream object pre-associated with a system's standard input, usually a computer keyboard.

12.2.2: Output Formatting

  • A manipulator adjusts the way that the insertion operator << or extraction operator >> output appears
  • To use manipulators, must #include <iomanip>
  • Two general categories of manipulators: floating-point and text-alignment
  • Floating-point number manipulators include:
    cout << fixed;           // Fixed notation
    cout << scientific;      // Scientific notation
    cout << showpoint;       // show decimal point
    cout << showpos;         // Show + signs
    cout << setprecision(d); // Digits; decimal places if fixed/scientific
    
  • Text-alignment manipulators include:
    cout << setw(n);    // number characters next output only
    cout << setfill(c); // Sets the fill to character c
    cout << left;       // Left justify
    cout << right;      // Right justify
    
  • All formatting changes affect all further output in program except as indicated above
  • In the following example, notice how setprecision() changes output depending on the mode: default vs fixed or scientific

Example Using Floating-Point Manipulators

#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    double number;
    cout << "Enter a number and I will format it: ";
    cin >> number;

    // Floating-point manipulators
    cout << "default:\t\t" << number << endl;
    cout << "scientific:\t\t" << scientific  << number << endl;
    cout << "fixed:\t\t\t" << fixed << number << endl;
    cout << "setprecision(3):\t" << setprecision(3) << number << endl;
    cout << "reset to default\n" << defaultfloat << setprecision(6);
    cout << "setprecision(3):\t" << setprecision(3) << number << endl;
    cout << "showpoint:\t\t" << showpoint << number << endl;

    return 0;
}

Example Using Text-Alignment Manipulators

#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    string str;
    cout << "Enter a few words and I will align text: ";
    getline(cin, str);

    // Text-alignment manipulators
    cout << "default:\t(" << str << ')' << endl;
    cout << "setw(25):\t(" << setw(25) << str << ')' << endl;
    cout << "setfill('*'):\t(" << setfill('*') << setw(25) << str << ')' << endl;
    cout << "left:\t\t(" << left << setw(25) << str << ')' << endl;
    cout << "right:\t\t(" << right << setw(25) << str << ')' << endl;
    cout << "reset to default\n" << right << setfill(' ');
    cout << "default:\t(" << str << ')' << endl;

    return 0;
}

Exercise 12.2: Coding Streams and Formatting (15m)

In this exercise we write code to neatly format output from a vector of objects.

For this exercise we break into teams. Within the team, work with each other to develop a solution. When the team has finished, choose one member to show your solution to the class by sharing your screen. The instructor will ask one team to share their solution.

Specifications

  1. Start Replit and copy the following code into the code editor.
    #include <iostream>
    #include <iomanip>
    #include <vector>
    using namespace std;
    
    class Car {
        public:
        Car();
        Car(string newName, double newPrice, double newMpg);
        void print() const;
    
        private:
        string name;
        double price;
        int mpg;
    };
    
    Car::Car() {
        name = "none";
        price = 0.0;
        mpg = 0.0;
    }
    
    Car::Car(string newName, double newPrice, double newMpg) {
        name = newName;
        price = newPrice;
        mpg = newMpg;
    }
    
    void Car::print() const {
        cout << name << " @ " << price << " with MPG " << mpg << endl;
    }
    
    int main() {
        Car myCar("Tesla Model 3", 35000, 134);
        Car car2("Toyota Corolla", 25000, 34);
        Car car3("Junker", 250, 7);
        vector<Car> carList = { myCar, car2, car3 };
        for (unsigned i = 0; i < carList.size(); i++) {
            cout << (i + 1) << " ";
            carList.at(i).print();
        }
    
        return 0;
    }
    
  2. Compile your code to make sure it has correct syntax. Run the code and verify you see:
    1 Tesla Model 3 @ 35000 with MPG 134
    2 Toyota Corolla @ 25000 with MPG 34
    3 Junker @ 250 with MPG 7
    
  3. Inside the print() function, add the following formatting constants for the setw() function calls we add.
        // Formatting constants
        const int NAME_WIDTH = 18;
        const int PRICE_WIDTH = 10;
        const int MPG_WIDTH = 6;
    
  4. Now add statements to print() to format the output such that the name, price and mpg columns neatly align like:
    1 Tesla Model 3       35000.00   134
    2 Toyota Corolla      25000.00    34
    3 Junker                250.00     7
    

    Notice the left-alignment of names and the right alignment of numbers, including the way decimal points line up.

  5. Inside main(), add one or more statements to add a title to each column. When finished, the output looks like:
    # Name                   Price   MPG
    1 Tesla Model 3       35000.00   134
    2 Toyota Corolla      25000.00    34
    3 Junker                250.00     7
    
  6. Once satisfied with your code, copy your code into a text editor, save the file as "carlist2.cpp", and submit the file to Canvas with the rest of the exercise files for the week.

When finished developing your code click hereClick to show answer to verify. Code need not look exactly the same. After you have completed your own program, reviewing another is often helpful in learning how to improve your programming skills.

12.3: Computer Files

A computer file is a collection of data stored under a common name, usually saved so it is available after a program finishes executing.

12.3.1: About Computer Files

  • Sometimes programmers need to access computer data even after the program creating the data finishes executing
  • Programs need a way to save (store) data rather than having to re-enter it
  • For durable storage of data we use computer files

    Computer File: a collection of data stored under a common name, usually saved so it is available after a program finishes executing.

  • Files offer many advantages to computer programs:
    • 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 files in C++, we need two items:
    • files
    • file streams
  • We will start with files

12.3.2: Types of Files

  • All data in a file is ultimately just zeros and ones (bits)
  • However, we classify the files based on how they store data into two broad categories: text and binary (everything but text)
  • For text files, the bits represent printable characters using ASCII or Unicode
  • An example of a text file is source code
  • Humans can read text files because each byte is interpreted by programs as textual characters
  • Some text-reading programs, like TextEdit or NotePad, then display the textual data to your computer's screen
  • One property of text files is they have newline characters to show the end of one line and the start of the next
  • Unfortunately, different operating systems have different newline characters:
    • Linux/Mac: "\n"
    • Windows: "\r\n"
  • Our version of C++ is installed on Linux and follows the Linux/Mac convention
  • Thus Windows users will need to translate text files to the Linux/Mac convention

12.3.3: Translating to Unix Line Endings

  • If we are using a Mac or Linux, then we automatically save in UNIX file format
  • If we are using Windows, we must purposely save all text data files in the UNIX file format using:
  • Also, we may convert a file using the Linux translate command:
    tr -d "\r" < infile.txt > outfile.txt
    

Exercise 12.3: Preparing Text Files (5m)

In this exercise we upload or install a data file to work with in Replit.

For this exercise we break into teams. Within the team, work with each other to solve any problems that occur.

Repli.it file tab

Specifications

  1. Start Replit and open or locate the Files tab, shown in the image to the right.

    You may need to sign up (free) and login to use the Files tab.

  2. Copy the contents of infile.txt to Replit by either:
    • Uploading the file (click the 3 vertical dots and choose upload)
    • Creating a new file on Replit and copying in the contents

    We will read from this file after writing our program.

  3. Try running the translate program to remove "\r" from the file by typing the following command into the terminal window:
    tr -d "\r" < infile.txt > outfile.txt
    

    If successful, you will see another new file appear in the File tab named "outfile.txt".

12.4: Files and Streams

To read and write files, a programmer uses input and output streams connected to files.

12.4.1: Reviewing Files and Streams

File stream: a data delivery path used to connect a program to a file.

  • File input stream: an object that provides a data path from a file to a program

    input stream

  • File output stream: an object that provides a data path from a program to a file

    output stream

  • 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
    fin.open("infile.txt"); // connects stream to the file
    
  • Similarly, to declare a file output stream we write code like:
    ofstream fout;  // declares a file output stream
    fout.open("outfile.txt"); // connects stream to the file
    
  • Each file your program uses will need a separate file stream object
  • After a file is open, good practice is to check it opened successfully
    if (!fin.is_open()) {
        cout << "Could not open file infile.txt." << endl;
        exit(1);
    }
    
  • The exit() function ends a program immediately and is part of the <cstdlib> library.
  • Examine 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 Code (Replit)

/**
    Reads three numbers from the file infile.txt,
    sums the numbers, and writes the sum to the
    file outfile.txt.
*/
#include <iostream>
#include <fstream>   // for file I/O
#include <cstdlib>   // for exit()
using namespace std;

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

    ofstream fout;
    fout.open("outfile.txt");
    if (!fout.is_open()) {
        cout << "Output file failed to open.\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;
}

12.4.2: Streams, Objects and Errors

  • Stream errors occur when insertion or extraction fails, causing the stream to enter an error state
  • For instance, in the following, we get incorrect results if the user enters "seven"
    double input = 0.0;
    fin >> input;
    
  • The problem is that fin cannot convert the word "seven" into the number 7
  • When this happens fin fails, sets an error flag and skips the rest of the input operation
  • We detect the failure condition using code like:
    if (fin.fail()) {
        cout << "The stream failed\n";
    }
    
  • Checking for errors is very important when performing file I/O (input and output)
  • See the following table for commonly used error functions

Commonly Used IO Stream Functions for Error Detection

Name Description
bad Returns true if a non-recoverable error has occurred on the stream.
eof Returns true if the stream has reached end-of-file.
fail Returns true if an error has occurred on the stream, including reaching the end of file.
good Returns true if the most recent I/O operation on the stream completed successfully.

12.4.3: 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.is_open()) {
        cout << "Input file failed to open.\n";
        exit(1);
    }
    
    fout.open("outfile.txt");
    if (!fout.is_open()) {
        cout << "Output file failed to open.\n";
        exit(1);
    }
    
  4. Read or write the data:
    • Read from a file with fin like using cin:
      double first, second, third;
      fin >> first;
      fin >> second;
      fin >> third;
      
    • Write to a file with fout like using cout:
      fout << "first = " << first << endl;
      
  5. Close the streams when finished reading and writing:
    fin.close();
    fout.close();
    

Exercise 12.4: Coding Files and Streams (10m)

In this exercise we write a program to read and write file data.

Remember to verify your code by compiling after each step.

For this exercise we break into teams. Within the team, work with each other to develop a solution. When the team has finished, choose one member to show your solution to the class by sharing your screen. The instructor will ask one team to share their solution.

Specifications

  1. Start Replit and copy the following code into the code editor.
    #include <iostream>
    using namespace std;
    
    int main() {
        // Enter your code here
    
        return 0;
    }
    
  2. Save the file infile.txt to the same folder as your program source code.

    We will read from this file after writing our program.

  3. Add the following include directives to your source code file:
    #include <fstream>   // for file I/O
    #include <cstdlib>   // for exit()
    
  4. Inside main(), declare names for the input and output streams:
    ifstream fin;
    ofstream fout;
    
  5. Add code to connect each stream to a file using open() and check for failure:
    fin.open("infile.txt");
    if (!fin.is_open()) {
        cout << "Input file failed to open.\n";
        exit(1);
    }
    
    fout.open("outfile.txt");
    if (!fout.is_open()) {
        cout << "Output file failed to open.\n";
        exit(2);
    }
    
  6. 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;
    
  7. 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;
    
  8. Close the streams when finished reading and writing:
    fin.close();
    fout.close();
    
  9. At the end of main() before return 0, add a cout statement like:
    cout << "All done!\n";
    
  10. 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 an output file.

  11. View the content of outfile.txt, which should look like:
    first = 10
    second = 20
    
    • In Replit verify outfile.txt was created and click on it to view the contents.
    • On Windows, verify outfile.txt was created and open it using TextPad, NotePad++ or WordPad to view the contents. Do NOT view the contents using NotePad as it will show the wrong format.
    • On the Mac, verify outfile.txt was created and open it using TextEdit.
  12. Once satisfied with your code, copy your code into a text editor, save the file as "copytwo.cpp", and submit the file to Canvas with the rest of the exercise files for the week.

When finished developing your code click hereClick to show answer to verify. Code need not look exactly the same. After you have completed your own program, reviewing another is often helpful in learning how to improve your programming skills.

12.5: Using Loops to Read Files

A program may need to read varying amounts of data from a file.

12.5.1: Reviewing Loops to Read Files

  • Sometimes we do not know how many data items are in a file
  • To solve this, the typical approach is to use a loop to process the file
  • We read from the file until we reach the end of the file or encounter an error
  • The basic IO stream library has several functions we may call

Commonly Used IO Stream Functions for Error Detection

Name Description
bad Returns true if a non-recoverable error has occurred on the stream.
eof Returns true if the stream has reached end-of-file.
fail Returns true if an error has occurred on the stream, including reaching the end of file.
good Returns true if the most recent I/O operation on the stream completed successfully.

12.5.2: Testing for End of File with Functions

  • We may use the IO stream functions as a loop condition to test for the end of a file like:
    double data;
    while (!fin.eof()) { // keep going until end of file
        fin >> data; // read the data
        if (!fin.bad()) { // is the data good?
            cout << data << endl; // process the data
        }
    }
    
  • In the above example, we call the eof() function to test we have not reached the end of the file
  • We may use other functions such as:
    double data;
    while (fin.good()) { // keep going while no errors
        fin >> data; // read the data
        if (fin.good()) { // is the data good?
            cout << data << endl; // process the data
        }
    }
    
  • Encountering an error includes reaching the end of a file
  • In the above examples, an error could occur after reading the data successfully such as reaching the end of file
  • The test if (fin.good()) may prevent us from processing the last value read
    if (fin.good()) { // is the data good?
        cout << data << endl; // process the data
    }
    
  • Thus we always need a blank line at the end of the input file to ensure reading the last value
  • Otherwise we may not process the last value read
  • In the following example we read from rawdata.txt

Example Code Checking for Error (Replit)

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

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

    double data, sum = 0;
    int count = 0;
    while (!fin.eof()) { // keep going until end of file
        fin >> data; // read the data
        if (!fin.bad() && !fin.eof()) { // is the data good?
            cout << "Read: " << data << endl;
            sum = sum + data;
            count++;
        }
    }
    cout << "average = " << (sum / count) << endl;
    fin.close();

    return 0;
}

12.5.3: Testing for Error with the >> Operator

  • It is possible to encounter an error during a read operation like:
    fin >> data;
    
  • fin >> data reports errors by returning false on extraction failures (same as !fin.fail())
  • We may use this returned value when reading from file streams as a test condition:
    ifstream fin;
    // Code to open stream and test for failure not shown
    double data;
    while (fin >> data) { // read data and test for errors
        cout << data << endl; // process the data
    }
    
  • Since the loop test condition both reads and tests data, we never enter the loop if there is an error

Example Code using Extraction Operator as Loop Test (Replit)

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

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

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

    return 0;
}

12.5.4: Using Loops with getline()

  • Function getline() returns true on success and false on error like the >> operator does
  • This property makes it possible to use getline() with streams inside test conditions:
    ifstream fin;
    // Code to open stream and test for failure not shown
    string line;
    while (getline(fin, line)) {
        // process input
    }
    
  • Since we are testing for failure after reading data, we do not need an extra if-statement as when testing with functions
  • The following example uses getline() to read lines from a file

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("rawdata.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;
}

Exercise 12.5: Reading Files with Loops(12m)

In this exercise we explore the use of loops to read files.

For this exercise we break into teams. Within the team, work with each other to develop a solution. When the team has finished, choose one member to show your solution to the class by sharing your screen. The instructor will ask one team to share their solution.

Specifications

  1. Start Replit and copy the following code into the code editor.
    #include <iostream>
    #include <fstream>   // for file I/O
    #include <cstdlib>   // for exit()
    using namespace std;
    
    int main() {
        // Enter code here
    
        return 0;
    }
    
  2. Save the file rawdata.txt to the same folder as your program source code.

    We will read from this file after writing our program. See Exercise 12.3 if unsure how to add the file to Replit.

  3. In main(), add code to declare an input stream named fin and to connect the stream to the input file "rawdata.txt". In addition, make sure you check for failure after calling open().

    For more information see the section Procedure For File I/O.

  4. First we read from the file using the following loop code:
    double nextNum;
    while (!fin.eof()) {
        fin >> nextNum;
        if (!fin.bad()) {
            cout << "Read: " << nextNum << endl;
        }
    }
    
  5. Add a statement after the above to close the input stream.
  6. Compile and run your code, then verify you see output like the following:
    Read: 12.34
    Read: -9.87654
    Read: 2.3131
    Read: -89.506
    Read: 12.3333
    Read: 92.8765
    Read: -123.457
    

    If you have problems, ask a classmate or the instructor for help as needed.

  7. Try adding a blank line at the end of rawdata.txt, and rerunning the program.

    Do you see any difference in output?

  8. Now change the loop code to read using the >> operator inside the test condition:
    while (fin >> nextNum) {
        cout << "Read: " << nextNum << endl;
    }
    
  9. Compile, run and verify you see the same output as before.
  10. Try adding and removing a blank line at the end of rawdata.txt, and rerunning the program.

    Do you see any difference in output? Add a comment to your code about which code technique seems superior.

    1. while (fin >> nextNum)
    2. while (!fin.eof()) {
          fin >> nextNum;
          if (!fin.bad()) {
             ...
          }
      
  11. Once satisfied with your code, copy your code into a text editor, save the file as "readwrite.cpp", and submit the file to Canvas with the rest of the exercise files for the week.

When finished developing your code click hereClick to show answer to verify. Code need not look exactly the same. After you have completed your own program, reviewing another is often helpful in learning how to improve your programming skills.

12.6: Reading and Writing File Data with Vectors

If a program needs to process the data in a file multiple times, it is faster to load the data into a vector.

12.6.1: Reading File Data into a Vector

  • Sometimes we want to process the data in a file several times
  • One way to do this is to load the data into a vector
  • Then we process the data as a list
  • Processing a list multiple times is much faster than reading from a file multiple times
  • The following example shows how to read from a file into a vector
  • We first save file infile.txt to the same folder as our program source code.

Example Code (Replit)

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

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

    // Load data into a vector
    vector<int> data;
    int value;
    while(fin >> value) {
        cout << "Read: " << value << endl;
        data.push_back(value);
    }
    fin.close();

    // Process vector data
    double sum = 0;
    int count = data.size();
    for (int i = 0; i < count; i++) {
        sum = sum + data.at(i);
    }
    cout << "average = " << (sum / count) << endl;

    return 0;
}

12.6.2: Writing File Data from a Vector

  • When we write from a vector to a file, we usually know in advance how many items to write
  • We typically write the number of elements that are in the vector
  • This foreknowledge makes a for-loop a better loop choice
  • The following example shows how to write a vector to an output file using a for-loop

Example Code (Replit)

#include <iostream>  // for cout
#include <iomanip>   // for setprecision
#include <fstream>   // for file I/O
#include <cstdlib>   // for exit()
#include <vector>
using namespace std;

void writeData(const vector<double>& data) {
    ofstream fout;
    fout.open("outfile.txt");
    if (!fout.is_open()) {
        cout << "Output file failed to open.\n";
        exit(-1);
    }
    fout << fixed << setprecision(2);
    for (unsigned i = 0; i < data.size(); i++) {
        fout << setw(10) << right << data.at(i) << endl;
    }
    fout.close();
}

int main() {
    // C++11 vector initializer list
    vector<double> data = { 12.94, -9.87654, 2.3131, -89.506, 12.9333,
        92.8765, -123.457, 42 };
    writeData(data);
    cout << "Done writing data to outfile.txt...\n";

    return 0;
}

Exercise 12.6: Reading and Writing File Data to and from a Vector (12m)

In this exercise we work with vectors while reading and writing data to files.

For this exercise we break into teams. Within the team, work with each other to develop a solution. When the team has finished, choose one member to show your solution to the class by sharing your screen. The instructor will ask one team to share their solution.

Specifications

  1. Start Replit and copy the following code into the code editor.
    #include <iostream>
    #include <iomanip>   // for setprecision
    #include <fstream>   // for file I/O
    #include <cstdlib>   // for exit()
    #include <vector>
    using namespace std;
    
    void readData() {
        ifstream fin;
        fin.open("rawdata.txt");
        if (!fin.is_open()) {
            cout << "Input file failed to open.\n";
            exit(-1);
        }
        double nextNum;
        while (fin >> nextNum) {
            cout << "Read: " << nextNum << endl;
        }
        fin.close();
    }
    
    int main() {
        readData();
    
        return 0;
    }
    
  2. Save the file rawdata.txt to the same folder as your program source code.

    We will read from this file after writing our program.

  3. Inside the readData() function parenthesis, add the following parameter:
    void readData(vector<double>& data);
    
  4. In main() before the function call, declare a vector of type double named data. Then add the data variable as an argument to the readData() function like:
    readData(data);
    
  5. Inside the while-loop braces { }, add the following statement to save the values read from the input file into the vector.
    data.push_back(nextNum);
    
  6. In main() after the function call, add the following cout statement:
    cout << "Vector data:\n";
    
  7. After the above statement, add a for-loop to display all the elements of the vector.
    for (unsigned i  = 0; i < data.size(); i++) {
        cout << data.at(i) << endl;
    }
    
  8. Compile and run your code to verify it works correctly. When run, you should see the data from the file displayed twice like:
    Read: 12.94
    Read: -9.87654
    Read: 2.3131
    Read: -89.506
    Read: 12.3333
    Read: 92.8765
    Read: -123.457
    Vector data:
    12.34
    -9.87654
    2.3131
    -89.506
    12.3333
    92.8765
    -123.457
    
  9. Add a function with the following prototype to your code.
    void writeData(const vector<double>& data);
    
  10. Call the writeData() function from main() just before return 0.
  11. In writeData(), add code to declare an output stream named fout and to connect the stream to the output file "neat.txt". In addition, make sure you check for failure after calling open().
  12. After opening the output file, add the following code:
    fout << fixed << setprecision(2);
    for (unsigned i = 0; i < data.size(); i++) {
        fout << setw(10) << right << data.at(i) << endl;
    }
    
  13. Add a statement after the above to close the output stream.
  14. Compile and run your code to verify it works correctly. When run, you should see the following data in the file neat.txt:
         12.34
         -9.88
          2.31
        -89.51
         12.33
         92.88
       -123.46
    
  15. Once satisfied with your code, copy your code into a text editor, save the file as "vectorfile.cpp", and submit the file to Canvas with the rest of the exercise files for the week.

When finished developing your code click hereClick to show answer to verify. Code need not look exactly the same. After you have completed your own program, reviewing another is often helpful in learning how to improve your programming skills.

12.7: Functions and Streams

We may write functions that use streams.

12.7.1: Reviewing File Names as Strings

  • We may use strings to open file streams
  • Instead of:
    fin.open("infile.txt");
    
  • We may use a string like:
    string filename = "infile.txt";
    fin.open(filename);
    
  • This is convenient when we want to use functions to read from files
  • The following example shows using a string to open a file

Example Using a string for File Names

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

void showFile(string filename) {
    string line;
    ifstream fin;
    fin.open(filename);
    if (!fin.is_open()) {
        cout << "Input file " << filename << "failed to open.\n";
        exit(1);
    }
    // Read and display file contents
    while(getline(fin, line)) {
        cout << line << endl;
    }
    fin.close();
}

int main() {
    string filename, line;

    cout << "Enter a file name: ";
    cin >> filename;
    showFile(filename);

    return 0;
}

12.7.2: Functions with Stream Parameters

  • Streams can be parameter data types in functions
  • Here is an example of an ifstream parameter:
    string readLine(ifstream& aStream) {
        string line;
        getline(aStream, line);
        return line;
    }
    
  • Similarly, we can have ofstream parameters:
    void writeLine(ofstream& aStream, string line) {
        aStream << line << endl;
    }
    
  • Notice that both streams are call-by-reference parameters
  • The reason is that the file stream object changes as the file is processed
  • The changes must be recorded in the file stream object to keep the file synchronized properly

Example Using Stream Parameters (Replit)

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

/**
    Reads a line from the istream.

    @param aStream the output stream.
    @return The line of text read.
*/
string readLine(ifstream& aStream) {
    string line;
    getline(aStream, line);
    return line;
}

/**
    Writes a line to the ostream.

    @param aStream the output stream.
    @param line The string to output.
*/
void writeLine(ofstream& aStream, string line) {
    aStream << line << endl;
}

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

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

    while (fin.good()) {
        string line = readLine(fin);
        if (fin.good()) {
            writeLine(fout, line);
        }
    }

    fin.close();
    fout.close();
    cout << "Done copying file...\n";

    return 0;
}

Testing the Stream

  • Notice the use of the if-statement inside the loop
    string line = readLine(fin);
    if (fin.good()) { // is the data good?
        writeLine(fout, line);
    }
    
  • If the read operation reaches the end of the file, we do not want to write an extra blank line to the output file
  • We prevent this problem by testing the stream after each read operation
  • Remember that fin.good() returns true if the last I/O operation was successful

Exercise 12.7: Coding Functions with Stream Parameters (15m)

In this exercise we explore passing an input stream to a function.

For this exercise we break into teams. Within the team, work with each other to develop a solution. When the team has finished, choose one member to show your solution to the class by sharing your screen. The instructor will ask one team to share their solution.

Specifications

  1. Start Replit and copy the following code into the code editor.
    #include <iostream>
    #include <fstream>   // for file I/O
    #include <cstdlib>   // for exit()
    using namespace std;
    
    int main() {
        // Enter code here
    
        return 0;
    }
    
  2. Save the file products.txt to the same directory as your program source code.

    We will read from this file after writing our program.

  3. Write a function with the following signature:
    void readFile(string filename);
    
  4. Inside the function readFile(), add code to declare an input stream named fin and to connect the stream to the input file using the string fileName parameter.
  5. Add the following code to function readFile() that reads all the values from the input file.
    while (fin.good()) {
        string name;
        double price;
        fin >> ws; // clear whitespace including newlines
        getline(fin, name);
        fin >> price;
        if (fin.good()) { // verify not end-of-file
            cout << name << " @ " << price << endl;
        }
    }
    
  6. Add a statement to close the input stream.
  7. In main(), call the readFile() function with:
    readFile("products.txt");
    
  8. Compile and run your code, then verify you see output like the following:
    Milk@3.95
    Bread@2.99
    Cheese@3.95
    
  9. Once satisfied with your code, copy your code into a text editor, save the file as "filelist.cpp", and submit the file to Canvas with the rest of the exercise files for the week.

When finished developing your code click hereClick to show answer to verify. Code need not look exactly the same. After you have completed your own program, reviewing another is often helpful in learning how to improve your programming skills.

12.8: Reading and Writing File Data to and from a Vector of Objects

In this section we look at how to read and write file data to and from a vector of objects.

12.8.1: Reading File Data into a Vector of Objects

  • Lets say we have a file with information about products organized like products2.txt:
    Milk
    3.95
    Whole-wheat bread
    2.99
    Cheddar cheese
    3.95
    
  • We want to read data from the file into a vector of Product objects
  • Object-oriented design principles say that an object should know how to read and write it's own data
  • Thus a good way to approach this problem is to add a read() function to the class
  • The read() function reads from an input file stream with code like:
    void Product::read(ifstream& fin) {
        fin >> ws; // clear whitespace including newlines
        getline(fin, name);
        fin >> price;
    }
    
  • We open the input stream in main() and pass the stream in the function call:
    ifstream fin;
    fin.open("products.txt")
    ...
    Product temp;
    temp.read(fin);
    
  • To read all the product data into a vector of objects, we use a loop as shown in the following example
  • To run the example, we read from the file: products2.txt

Example Reading a File into a Vector of Objects (Replit)

#include <fstream>
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;

class Product {
public:
    Product();
    Product(string newName, double newPrice);
    void read(ifstream& fin);
    void print() const;
private:
    string name;
    double price;
};

Product::Product() {
    name = "Unknown";
    price = 0.0;
}

Product::Product(string newName, double newPrice) {
    name = newName;
    price = newPrice;
}

void Product::print() const {
    cout <<  name << " @ " << price << endl;
}

void Product::read(ifstream& fin) {
    fin >> ws; // clear whitespace including newlines
    getline(fin, name);
    fin >> price;
}

// Read from filename into the vector
void readFile(vector<Product>& list, string filename);

// Display vector data
void listProducts(const vector<Product>& list);

int main() {
    vector<Product> list;
    readFile(list, "products2.txt");

    cout << "\nProducts in my store:\n";
    listProducts(list);

    return 0;
}

void readFile(vector<Product>& list, string filename) {
    ifstream fin;
    fin.open(filename);
    if (!fin.is_open()) {
        cout << "Input file failed to open.\n";
        exit(-1);
    }

    while(fin.good()) {
        Product temp;
        temp.read(fin);
        if (fin.good()) {
            list.push_back(temp);
        }
    }
    fin.close();
}

void listProducts(const vector<Product>& list) {
    for (unsigned i = 0; i < list.size(); i++) {
        Product temp = list.at(i);
        temp.print();
    }
}

12.8.2: Writing File Data from a Vector of Objects

  • After reading data into a vector of objects, we may want to write the data to a file
  • As with reading, an object should know how to write its own data
  • Thus a good way to approach this problem is to add a write() function to the class
  • The write() function writes to an output file stream with code like:
    void Product::write(ofstream& fout) {
        fout << name << endl;
        fout << price << endl;
    }
    
  • We open the input stream in another function and pass the stream in the function call:
    void writeFile(vector<Product>& store, string fileName)
    {
        ofstream fout;
        fout.open(fileName);
        if (!fout.is_open())
        {
            cout << "Output file " << fileName << " failed to open.\n";
            exit(-1);
        }
        fout << fixed << setprecision(2); // two decimal places
        for (unsigned i = 0; i < store.size(); i++)
        {
            store.at(i).write(fout);
        }
        fout.close();
    }
    
  • To write all the product data into a vector of objects, we use a loop as shown in the above example
  • Notice that writing to a file is like output to a terminal window
  • We know in advance how many sets of data to write
  • Thus the loop for writing is a for-loop

Exercise 12.8: Read and Write File Data to and from a Vector of Objects (20m)

For this exercise we break into teams. Within the team, work with each other to develop a solution. When the team has finished, choose one member to show your solution to the class by sharing your screen. The instructor will ask one team to share their solution.

In this exercise we explore reading a file into a vector of objects. Start with the following Product class.

#include <fstream>
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;

class Product {
public:
    Product();
    Product(string newName, double newPrice);
    void print() const;
private:
    string name;
    double price;
};

Product::Product() {
    name = "none";
    price = 0.0;
}

Product::Product(string newName, double newPrice) {
    name = newName;
    price = newPrice;
}

void Product::print() const {
    cout <<  name << " @ " << price << endl;
}

// Read product data from a file.
void readFile(vector<Product>& list, string filename);

// List the products in the store.
void listProducts(const vector<Product>& store);

// Write vector of objects to the file.
void writeFile(vector<Product>& store, string fileName);

int main() {
    vector<Product> list;
    int choice;
    do { // simple menu
        cout << "\nSelect an option:\n";
        cout << "0. Exits program.\n";
        cout << "1. Load data from file.\n";
        cout << "2. Print data in vector.\n";
        cout << "3. Write data to a file.\n";
        cout << "Choice: ";
        cin >> choice;
        if (choice == 1) {
            // readFile(list, "products2.txt");
        } else if (choice == 2) {
            listProducts(list);
        } else if (choice == 3) {
            // writeFile(list, "saved.txt");
        } else if (choice != 0) {
            cout << "Please enter a number from 0 - 3.\n";
        }
    } while (choice != 0);
    cout << "Goodbye.\n";

    return 0;
}

void listProducts(const vector<Product>& list) {
    for (unsigned i = 0; i < list.size(); i++) {
        Product temp = list.at(i);
        temp.print();
    }
}

Specifications

  1. Start Replit and copy the above code into the code editor.
  2. Compile your code to make sure you copied it correctly.
  3. In the same directory (folder) as your source code, save the data file: products2.txt.
  4. In the Product class, add a read function with the following prototype:
    void read(ifstream& fin);
    
  5. Outside the Product class, add the implementation of the read function using the prototype just added.
    void Product::read(ifstream& fin) {
        // read the whitespace before getline
        // read the product name
        // read the price
    }
    
  6. Implement the readFile() function using the declared prototype from the starter code and the following pseudocode.
    void readFile(vector<Product>& list, string filename) {
        // open an input file stream
        // test if the stream failed to open
        // while the file stream is good
            // construct a temporary object
            // call the read() function on the object
            // if no error during read()
                // then push onto back of vector
        // close the stream after the loop ends
    }
    
  7. Call readFile() from main() by uncommenting the menu code like:
    readFile(list, "products2.txt");
    
  8. Compile and run your code to verify it works. When run, you should see the data from the file displayed like the following. Numbers in aqua italics show the input and are NOT part of the code to write.
    Select an option:
    0. Exist program.
    1. Load data from file.
    2. Print data in vector.
    3. Write data to a file.
    Choice: 1
    
    Select an option:
    0. Exist program.
    1. Load data from file.
    2. Print data in vector.
    3. Write data to a file.
    Choice: 2
    Milk @ 3.95
    Whole-wheat bread @ 2.99
    Cheddar cheese @ 3.95
    
    Select an option:
    0. Exist program.
    1. Load data from file.
    2. Print data in vector.
    3. Write data to a file.
    Choice: 4
    Please enter a number from 0 - 3.
    
    Select an option:
    0. Exist program.
    1. Load data from file.
    3. Write data to a file.
    Choice: 0
    Goodbye.
    
  9. After reading from a file works correctly, in the Product class, add a write function with the following prototype:
    void write(ofstream& fout);
    
  10. Outside the Product class, add the implementation of the write function using the prototype just added.
    void Product::write(ofstream& fout) {
        // write the product name
        // write the price
    }
    
  11. Implement the writeFile() function using the declared prototype from the starter code and the following pseudocode.
    void writeFile(vector<Product>& list, string filename) {
        // open an output file stream
        // test if the stream failed to open
        // for each object in the vector
            // call the write function
        // close the stream after the loop ends
    }
    

    Write an endl after each output command.

  12. Call writeFile() from main() by uncommenting the menu code like:
    writeFile(list, "saved.txt");
    
  13. Compile and run your code to verify it works as before. Select menu 3 to write to a file. Open the "saved.txt" output file and verify it is the same as the original "products2.txt" file.
  14. Once satisfied with your code, copy your code into a text editor, save the file as "productfile.cpp", and submit the file to Canvas with the rest of the exercise files for the week.

When finished developing your code click hereClick to show answer to verify. Code need not look exactly the same. After you have completed your own program, reviewing another is often helpful in learning how to improve your programming skills.

Deliverables

For this week's exercises we are turning in these files:

Remember that when submitting multiple files to Canvas, you must submit all files in the same session. For instructions on submitting multiple files for an assignment see the Canvas guide on How do I upload a file as an assignment submission in Canvas? section: Submit Assignment.

Lab Introductions

Here are a few tips on how to get started on the labs.

 

Last Updated: November 16 2021 @19:17:46