Schedule‎ > ‎

Assignment 12 Storing Information


Objectives

  • Load and save data in text files.
  • Explore code reuse in classes.
  • Code member functions that read data from and write data to file stream parameters.
  • Overload member functions.
  • Write non-member functions that open and close file streams.


Academic Honesty

Read the Scholastic Honesty Policy and Assignment Integrity policies of the syllabus. Here are some clarifications for this particular assignment:
  1. Most importantly, you undermine your own self confidence when you cheat, because you know you didn't accomplish the assignment without cheating.
  2. You may not give/show/post a copy of your code to anyone else.
  3. You may not look at another student's code until you complete and submit this assignment.
  4. You may get help from other people but only if they do not show or tell you the code to type.
  5. Remember that the instructor performs similarity tests on programming project submissions, and copied or plagiarized code is usually very easy to detect.


Grading Criteria (30pts + Extra Credit 5 pts)

For each part: 15 pts: File Header 1pt, Function Headers 1pt, Compiles 2pts, Proper Formatting 1pt, Works as Specified 10pts

  1. filework.cpp (15pts)
  2. contactfile.cpp & contacts.txt (15pts) +2pts for using your own contacts file
  3. Extra Credit: xccontactfile.zip (3 pts)
  4. Extra Credit:  xccontactfile2.cpp (2pts)

Part 2: You must put in the comment area a note that you used your own contacts file to start with for the extra 2 points.

Total Possible: 30 pts + Extra Credit 5 pts


Project Specifications

Your solutions to these projects must use only techniques we have covered so far.

Programming Style

For all programs, remember to follow all the style rules we have covered, as well as the newer rules, including:

  1. Class naming conventions (See: Class Names)
  2. Indentation in classes and placement of curly braces
  3. Every nontrivial function declaration (prototype) in the class, including constructor functions, have a function comment block
  4. Every file has a file comment block
  5. No magic numbers




Project 1: File Worksheet

Files are a way to store data that is retained even after a program ends or a computer is turned off. To save data we

write to a file. To recover the data, we read from the file.

Use this worksheet to improve your understanding of files, vectors and functions. Refer to lesson 11 and lesson 12 for more information on files.

Project Specifications
  1. Start by downloading the files:
    • filework.cpp: worksheet to code
    • helper.cpp: worksheet helper (see lesson 12.4.1 for information on including files)

    Keep the same filenames and add to the existing code in filework.cpp to complete the project. Leave the existing code unchanged, except for comments as instructed.

  2. Add your name and the date to the file comment block at the top of the filework.cpp file, replacing the words Your Name with your first and last name and Date Here with the current date.
  3. No user input is required for this project and do not add any. All data is read from the files.
  4. Write the required functions as described by the function signature and comment block.

    Do NOT change any of the function signatures.

  5. Write the function definitions below main(), and their prototypes above main().
  6. Compile and run the code when finished with each function to verify correctness.

    Uncomment the function calls from main() as you progress. Do NOT change any code in main() beyond removing comments to run the code. 

  7. Submit the filework.cpp file of this project with the rest of the assignment.  

    You do not need to submit the helper.cpp file as I have it already.

Hints:
  • Remember the 5 step Procedure For File I/O in lesson 11.2.5.
  • Remember that reading from a file is like reading data with cin. Solve the problem as you would by asking the user for data and then change cin to fin (or whatever stream name you chose).



Project 2: Contacts File

We continue to develop out contact list manager application. We load our data from a file and save our vector of Person objects to file so the list is retained between uses of the application. The data we work with is automatically loaded when the program starts and saved when the program exits.

When developing this program, you will use your Contact List code from Assignment 10. You will add new files to store data for our application.                                          (Image by Hopstarter)

Project Specifications
  1. Using your contacts.cpp source code from Assignment 10 (you get an extra 2 points for using your own contacts.cpp)
  2. You have to note in the comment area that you used your own contacts file to start with to get the 2 points.
  3. If you don't have your own contacts.cpp you can use this HERE
  4. Save your file as contactfile.cpp  You must include all your code in this single file.
  5. Your updated program must still operate like assignment 10 when you are done making the following changes.
  6. Fix any problems with your program identified by your instructor during grading.
  7.  You will update your application to load and save the data of your contacts list in files.

    Keep the Person class without adding non-specified variables or removing any of the required functions from the previous assignment  . Adding member functions is allowed if appropriate. Make sure to correct any errors or problems identified in the last two assignments including incorrect style. Also, make sure to #include <climits> if you use INT_MAX and #include <cstdlib> if you use exit() to ensure the program compiles on all computers.

  8. Add a second read() member function to your Person class that reads the next set of data from a file input stream and loads the data into the member variables of the Person object in the following order:
    1. name
    2. age
    3. income

    Use the following function prototype exactly for this function:

    /**
        Loads information about this Person from the file stream.
    
        @param fin A file input stream connected to the files with the
        data to load.
    */
    void read(ifstream& fin);
    

    This function is like the read() function you wrote in assignment 10, except for the use of an input stream like fin rather than cin. For an example see lesson lesson 12.3.5. Note that this function must be able to read people's names that include single or multiple words with spaces between the words. Keep the original read() function from the last assignment.

  9. Add a non-member function named loadData() to your program that loads contact data from a file and returns the loaded data in a vector of Person objects.

    Use the following prototype exactly for this function:

    /**
        Loads all contact data from the specified file name and
        returns the data in a vector of Person objects.
    
        @param list The list of contacts read from the file.
        @param fileName The name of the file from which to read.
    */
    void loadData(vector<Person>& list, string fileName);
    

    The function must call the read(ifstream&) function from the previous step. For an example of reading file data into a vector of objects, see lesson lesson 12.3.5. When opening the file, make certain to call the c_str()member function of string to ensure compatibility with C++98.

  10. Remove your hard-coded contacts and load from the data file instead. Call the loadData() function with an argument of "contacts.txt" when the program starts so that it loads the file automatically before the menu and without any user input.
  11. You must prepare and submit a contacts.txt file and your contacts.txt must follow the format of this file: contacts.txt. You must change the names/age/income in the contacts.txt file.  The data for each contact is contained in groups of three lines each in the order:
    1. person's name
    2. age in years
    3. annual income in dollars and cents

    Make sure to save contacts.txt in the UNIX file format and a blank line at the end of the file. Notice that a person's names may have spaces. Thus you must use getline() to read each person's name.

  12. Add a write() member function to your Person class that writes the member variables to a file output stream in the following order, each on a new line:
    1. name
    2. age
    3. income
    Use the following function prototype exactly for this function:
    /**
        Writes information about this Person to the file stream.
    
        @param fout A file output stream connected to the file in
        which to save the data.
    */
    void write(ofstream& fout) const;
    
  13. Make a copy of your original contacts.txt in case you run into problems that prevent loading the data when your program starts.
  14. Add a non-member function named saveData() to your program that stores a vector of Person data into a file. Use the following prototype exactly for this function:
    /**
        Writes contact data to an output file.
    
        @param list The vector of Person objects.
        @param fileName The name of the file to which to write.
    */
    void saveData(const vector<Person>& list, string fileName);
    

    This function must call the write() function from the previous step. This function is like listing to the console but calls write() instead of print(). See lessons 10.4.1 and 10.4.2 for examples of printing and listing vectors data. When opening the file, make certain to call the c_str() member function of string to ensure compatibility with C++98.

  15. Call the saveData() function when the program exits so that all the current catalog data is saved to the file contacts.txt automatically without any user input.

    The output of the saveData() function must save the data in the same format as the original input file. For instance, do NOT append the data and do NOT format the numbers differently. Notice the output file name is the same name as  the input file name. The purpose of this is to store all your latest Person information and load it the next time you run your program.  Make a copy of your original contacts.txt in case you run into problems that prevent loading the data when your program starts.

  16. Submit this project with the rest of the assignment.
Hints:
  • For how to save in the UNIX file format, see lesson 11.2.4.  Notice that person names may have spaces.
  • For an example of reading file data into a vector of objects, see lesson 12.3.5.
  • Do not forget about using: fin >> ws to clear whitespace from the input buffer.


Extra Credit (5 pts)

The following are worth extra credit points:

  1. (3 pts)  Separate your code into 3 files,  an interface contactfile.h file,  an implementation contactfile.cpp file and an appcontact.cpp file.  Provide a Makefile that will build this.  Submit all 4 files as a zip file to Canvas as part of this Assignment.   Note:  to zip a set of files,  in Windows:  select the files,  right click,  send to->compressed(zipped) folder.  You must name  your zip file xccontactfile.zip
  2. (2 pts) Write a non-member function using the following prototype starting with your contactfile.cpp: 
    1. Call your new file xccontactfile2.cpp
      /**
          Writes a report to an output file (extra credit).
      
          @param list The vector of Person objects.
          @param fileName The name of the file to which to write.
      */
      void writeReport(vector<Person> list, string fileName);
      

      Add a new menu item 99 to main and call the writeReport() function from main() to produce another text file named report.txt formatted like the following:

      Contacts list report:
       # Name                 Age     Income
       1 Sophie Engineer       42  102280.00
       2 Emma Hacker           24   71916.00
       3 John Q Public         37   55775.37
      Number of contacts: 3
      

         You must submit a file called xccontactfile2.cpp to get Extra Credit.


SOLUTION CODE:

filework.cpp

/**
    filework.cpp
    Purpose: a worksheet to improve your skills with files, functions, objects
    and vectors.

    @author Sharon Strelitz
    @version 1.0 12/25/2018
 */
#include <cstdlib>   // for exit()
#include <fstream>   // for file I/O
#include <iostream>  // for cout
#include <vector>    // for vectors
#include "helper.cpp" // worksheet helper, see 12.4.1
using namespace std;

/**
    Read a file of integers and returns the data via reference parameters.

    @param myInts The vector of integers to read into.
    @param fileName The name of the file to read from.
    @see: Lesson 12.1.2, Exercise 12.1
*/
void readInts(vector<int>& myInts, string fileName);

/**
    Read a file of strings and returns the data via reference parameters.

    @param myStrs The vector of integers to read into.
    @param fileName The name of the file to read from.
    @see: Lesson 12.1.2, 12.1.4
*/
void readStrs(vector<string>& myStrs, string fileName);

/**
    Read a file of strings with spaces and returns the data via reference
    parameters.

    @param myNames The vector of integers to read into.
    @param fileName The name of the file to read from.
    @see: Lesson 12.1.3
*/
void readNames(vector<string>& myNames, string fileName);

/**
    Opens the file and calls Product::read(ifstream&) in a while-loop
    to read all the product names and prices from the file, storing the
    data in the single vector<product> myProds.

    @param myProds The vector of integers to read into.
    @param fileName The name of the file to read from.
    @see: Lesson 12.3.5, Exercises 12.2, 12.3
*/
void readProds(vector<Product>& myProds, string fileName);

int main() {
    vector<int> myInts;
    vector<string> myStrs;
    vector<string> names;
    vector<Product> prods;
    setup(); // create files: ints.txt, strs.txt, names.txt, prods.txt

/* Remove comments as you develop functions.*/
    cout << "\n***Testing readInts***"<< endl;
    readInts(myInts, "ints.txt");
    cout << "readInts vector size should be 3: " << myInts.size() << endl;
    cout << "readInts0 should be 27: " << myInts[0] << endl;
    cout << "readInts1 should be 39: " << myInts[1] << endl;
    cout << "readInts2 should be 42: " << myInts[2] << endl;


/* Remove comments as you develop functions.  */
    cout << "\n***Testing readStrs***"<< endl;
    readStrs(myStrs, "strs.txt");
    cout << "readStrs vector size should be 3: " << myStrs.size() << endl;
    cout << "readStrs0 should be C++: " << myStrs[0] << endl;
    cout << "readStrs1 should be still: " << myStrs[1] << endl;
    cout << "readStrs2 should be rules: " << myStrs[2] << endl;

/* Remove comments as you develop functions.*/
    cout << "\n***Testing readNames***"<< endl;
    readNames(names, "names.txt");
    cout << "readNames vector size should be 4: " << names.size() << endl;
    cout << "readNames0 should be Sophie Engineer: " << names[0] << endl;
    cout << "readNames1 should be Emma Hacker: " << names[1] << endl;
    cout << "readNames2 should be John Q Public: " << names[2] << endl;
    const int IDX3 = 3;
    cout << "readNames3 should be Joe Schmoe: " << names[IDX3] << endl;


/* Remove comments as you develop functions. */
    cout << "\n***Testing readProds***"<< endl;
    readProds(prods, "products.txt");
    cout << "readProds vector size should be 3: " << prods.size() << endl;
    cout << "readProds0 should be Milk @ 3.95: ";
    prods[0].print();
    cout << "readProds1 should be Fresh bread @ 2.99: ";
    prods[1].print();
    cout << "readProds2 should be Cheddar cheese @ 4.99: ";
    prods[2].print();
    cout << "\n***End of Tests***" << endl;
    return 0;
}

void readInts(vector<int>& myInts, string fileName) {
    ifstream fin(fileName);
    if (fin.fail())  {
        cout << "Input file failed to open.\n";
        exit(-1);
    }
    int input;
    while (fin >> input)  {
        myInts.push_back(input);
    }
}

void readStrs(vector<string>& myStrs, string fileName) {
    ifstream fin(fileName);
    if (fin.fail())  {
        cout << "Input file failed to open.\n";
        exit(-1);
    }
    string input;
    while (fin >> input)  {
        myStrs.push_back(input);
    }
}

void readNames(vector<string>& myNames, string fileName)  {
    ifstream fin(fileName);
    if (fin.fail())  {
        cout << "Input file failed to open.\n";
        exit(-1);
    }
    string input;
    while (getline(fin, input))  {
        myNames.push_back(input);
    }
    
}

void readProds(vector<Product>& myProds, string fileName)  {
    ifstream fin(fileName);
    if (fin.fail())  {
        cout << "Input file failed to open.\n";
        exit(-1);
    }
    Product tempProd;
    while (fin.good())  {
        tempProd.read(fin);
        if (fin.good()) { // verify not end-of-file
            myProds.push_back(tempProd);
        }    
    }
}

contactfile.cpp

/**
    CS-11 Asn 12, contactfile.cpp
    Purpose: keeping track of people and uses files
             to store and load the people

    @author  Sharon Strelitz
    @version 1.0 10/22/2017
*/
#include <vector> 
#include <iostream>
#include <iomanip>
#include <sstream> // for extra credit
#include <fstream>
using namespace std;

class Person {
public:
    /**
        Default constructor assigns default values.
    */
    Person();

    /**
        Constructs a Person object.

        @param name Name of the person.
        @param income Income to the Earth in miles.
        @param age Number of new age for this person.
    */
    Person(string name, int age, double income);

    /**
        Returns the name of this person.

        @return The name of this person.
    */
    string getName() const;

    /**
        Returns the age of this person.

        @return The age of this person.
    */
    int getAge() const;

    /**
        Returns the income of this person.

        @return The income of this person in miles.
    */
    double getIncome() const;

    /**
        Changes the name of this person.

        @param newName New name of this person.
    */
    void setName(string newName);

    /**
        Changes the age of this person.

        @param newAge New age of this person.
    */
    void setAge(int newAge);

    /**
        Changes the income of this person.

        @param newIncome New income of this person.
    */
    void setIncome(double newIncome);

    /**
        Displays information about this person to the screen.
    */
    void print() const;

    /**
        Reads person data from the keyboard.
    */
    void read();

     /**
    Loads information about this Person from the file stream.

    @param fin A file input stream connected to the files with the
    data to load.
    */
    void read(ifstream& fin);
    
    /**
    Writes information about this Person to the file stream.

    @param fout A file output stream connected to the file in
    which to save the data.
    */
    void write(ofstream& fout) const;

private:
    string name;    // name of person
    int age;        // age of person
    double income;  // income of person
};

// Formatting constants
const int NAME_WIDTH = 18;
const int AGE_WIDTH = 6;
const int INCOME_WIDTH = 11;

// Default constructor
Person::Person() {
    age = 0;
    income = 0.0;
}

// Overloaded constructor
Person::Person(string name, int age, double income) {
    setName(name);
    setAge(age);
    setIncome(income);
}

// Accessor functions
string Person::getName() const {
    return name;
}

int Person::getAge() const {
    return age;
}

double Person::getIncome() const {
    return income;
}

void Person::print() const {
    cout << setw(NAME_WIDTH) << left << name
         << right << setw(AGE_WIDTH) << age
         << fixed << setprecision(2) << setw(INCOME_WIDTH) << income
         << endl;
}

// Mutator functions
void Person::setName(string newName) {
    name = newName;
}

void Person::setAge(int newAge) {
    if (newAge >= 0) {
        age = newAge;
    } else {
        age = 0;
        cout << "Age cannot be negative; setting to zero.\n";
    }
}

void Person::setIncome(double newIncome) {
    income = newIncome;
}

void Person::read() {
    cout << "Enter the name of the person: ";
    cin >> ws;
    getline(cin, name);
    // Making use of the extra credit function
    cout << "Enter the age for " << name << ": ";
    cin >> age;
    cout << "Enter the income for " << name << ": ";
    cin >> income;
}

void Person::read(ifstream& fin) {
    fin >> ws;
    getline(fin, name);
    fin >> age;
    fin >> income;    
}

void Person::write(ofstream& fout) const  {
    fout << name  << endl;
    fout << age << endl;
    fout << fixed << setprecision(2);
    fout << income << endl;
}
/**
    Lists the people on the list.

    @param list The list of Person objects.
*/
void listContacts(vector<Person>& list);

/**
    Adds a person to the list.

    @param list The list of Person objects.
*/
void addContact(vector<Person>& list);

/**
    Deletes a person from the list.

    @param list The list of Person objects.
*/
void deleteContact(vector<Person>& list);

/**
    Changes the income for a contact.

    @param list The list of Person objects.
*/
void changeIncome(vector<Person>& list);

/**
    List contacts within an age bracket.

    @param list The list of Person objects.
*/
void listByAge(vector<Person>& list);

/**
    Loads all contact data from the specified file name and
    returns the data in a vector of Person objects.

    @param list The list of contacts read from the file.
    @param fileName The name of the file from which to read.
*/
void loadData(vector<Person>& list, string fileName);

/**
    Writes contact data to an output file.

    @param list The vector of Person objects.
    @param fileName The name of the file to which to write.
*/
void saveData(const vector<Person>& list, string fileName);

int main() {
    // Menu items
    const int LIST = 1;
    const int ADD = 2;
    const int DEL = 3;
    const int CHG = 4;
    const int AGE = 5;
    const int EXIT = 0;
    // Other constants
    /*
    const int AGE1 = 42;
    const int AGE2 = 24;
    const int AGE3 = 37;
    const double MEDIAN_SOFT_DEV = 102280.0;
    const double AVG_CS_NEWGRAD = 71916.0;
    const double AVG_US = 55775.37;
    Person sophie;
    sophie.setName("Sophie Engineer");
    sophie.setAge(AGE1);
    sophie.setIncome(MEDIAN_SOFT_DEV);
    Person emma("Emma Hacker", AGE2, AVG_CS_NEWGRAD);
    Person john("John Q Public", AGE3, AVG_US);

    vector<Person> list;
    list.push_back(sophie);
    list.push_back(emma);
    list.push_back(john);
*/
    vector<Person> list;
    loadData(list,"contacts.txt");
    cout << "Welcome to the Contact List Manager\n";
    int choice = 1;
    while (choice != 0) {
        string msg = "\nPlease choose one of the following operations:\n";
        msg += "0. Exit program\n";
        msg += "1. List contacts\n";
        msg += "2. Add a contact\n";
        msg += "3. Delete a contact\n";
        msg += "4. Change contact income\n";
        msg += "5. List by age\n";
        msg += "Choice (0-5): ";
        cout << msg;
        cin >> choice;
        if (choice == LIST) {
            listContacts(list);
        } else if (choice == ADD) {
            addContact(list);
        } else if (choice == DEL) {
            deleteContact(list);
        } else if (choice == CHG) {
            changeIncome(list);
        } else if (choice == AGE) {
            listByAge(list);
        } else if (choice != EXIT) {
            cout << "\nInvalid choice!\n";
        }
    }
    saveData(list, "contacts.txt");
    cout << "\nGoodbye!\n";

    return 0;
}

void listContacts(vector<Person>& list) {
    const int HASH_WIDTH = 3;
    cout << endl;
    cout << "Contact list:\n";
    cout << setw(NAME_WIDTH + HASH_WIDTH) << left << " # Name"
         << setw(AGE_WIDTH) << right << "Age"
         << setw(INCOME_WIDTH) << right << "Income" << endl;
    for (unsigned num = 0; num < list.size(); num++) {
        cout << setw(2) << right << (num + 1) << " ";
        list[num].print();
    }
}

// Add person to list by calling read()
void addContact(vector<Person>& list) {
    cout << "\nAdding a new contact:\n";
    Person peep;
    peep.read();
    list.push_back(peep);
}

void deleteContact(vector<Person>& list) {
    cout << "\nDeleting a contact:";
    listContacts(list);
    cout << "Enter the number of the contact: ";
    int pos;
    cin >> pos;
    pos--; // adjust for vector starting at 0
    for (unsigned i = pos; i < list.size() - 1; i++) {
        list[i] = list[i + 1];
    }
    list.pop_back();
}

void changeIncome(vector<Person>& list) {
    cout << "\nChange income for a contact:";
    listContacts(list);
    cout << "Enter the number of the contact: ";
    int pos;
    cin >> pos;
    pos--; // adjust for vector starting at 0
    double income = list[pos].getIncome();
    cout << "Enter the new income: ";
    cin >> income;
    list[pos].setIncome(income);
}

void listByAge(vector<Person>& list) {
    int min = 0;
    int max = list.size() - 1;
    cout << "\nSearching by Age\n";
    cout << "Enter the minimum age: ";
    cin >> min;
    cout << "Enter the maximum age: ";
    cin >> max;

    cout << setw(NAME_WIDTH) << left << "Name"
         << setw(AGE_WIDTH) << right << "Age"
         << setw(INCOME_WIDTH) << right << "Income" << endl;
    for (unsigned i = 0; i < list.size(); i++) {
        int age = list[i].getAge();
        if (age >= min and age <= max) {
            list[i].print();
        }
    }
}

void loadData(vector<Person>& list, string fileName) {
    ifstream fin(fileName.c_str());
    if (fin.fail())  {
        cout << "error opening input file" << endl;
        exit(-1);
    }
    while(fin.good())  {
        Person temp;
        temp.read(fin);
        if (fin.good())  {
            list.push_back(temp);
        }
    }
    fin.close();
}

void saveData(const vector<Person>& list, string fileName) {
    ofstream fout(fileName.c_str());
    if (fout.fail())  {
        cout << "error opening output file" << endl;
        exit(-1);
    }
    fout << fixed << setprecision(2); // two decimal places
    for (int i = 0;  i < (int) list.size(); i++)  {
        list[i].write(fout);
    }
    fout.close();
}




Subpages (2): contacts.cpp contacts.txt
Comments