6
\$\begingroup\$

The program will track the monthly budget of the user and will then compare it to the expenses of the user. The data members of the structs will be sent to binary files. Once the results are displayed to the user, the results will be sent to a text file. Also be advised, My teacher is very old schooled so I apologize if some of the methods I've used are out of date.

#include <cstdlib>
#include <limits>
#include <iostream>
#include <iomanip> 
#include <string>
#include <fstream>
#include <cmath>
#include <cctype>
using namespace std;

struct MonthlyBudget
{
    double housing;
    double utilites;
    double houseHold;
    double transportation;
    double food;
    double medical;
    double insurance;
    double entertainment;
    double clothinng;
    double misc;
};

struct MonthlyExpenses
{
    double housingEx;
    double utilitesEx;
    double householdEx;
    double transportationEx;
    double foodEx;
    double medicalEx;
    double insuranceEx;
    double entertainmentEx;
    double clothinngEx;
    double miscEx;
};

//prototypes declared outside of main so main function logic isn't distorted
void describeProgram();
void getMonths(int& months);
void getMonthlyBudget(fstream& budgetFile, int months);
void getMonthlyExpenses(fstream& expenseFile, int months);
void getMonthlyReport(fstream& budgetFile, fstream& expenseFile, int months);   

/**
 * 
 * @return 0
 */
int main() 
{
    int months = 1;
    fstream budgetFile, expenseFile;   

    describeProgram();
    getMonths(months);
    getMonthlyBudget(budgetFile, months);
    getMonthlyExpenses(expenseFile, months);
    getMonthlyReport(budgetFile, expenseFile, months);

    return 0;
}

/**
 * 
 */
void describeProgram()
{
    cout<<
          "This program will ask you for your desired budget for housing, "
          "utilites, entertainment,  \n etc (8 budget options), and will also ask you for the amount"
          " of money you spent on such expenses. \n First, the program will ask you for how "
          "many months you would like analyzed. These numbers entered will be put into "
          "a \n MonthlyBudget struct and sent to two external files (budget.bin ad expenses.bin). "
          "It will then calculate the difference and display the budget,  \n the expenses, "
          "and over/under in a tabular format.\n";
}

/**
 * 
 * @param months
 */
void getMonths(int& months)
{
    cout<<"How many months would you like to have analyzed? ";
    cin>>months;  

    while(cin.fail())
    {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(),'\n');
        cout << "Enter valid input ";
        cin >> months;
    }

}

/**
 * 
 * @param budgetFile
 * @param months
 */
void getMonthlyBudget(fstream& budgetFile, int months)
{
    MonthlyBudget mb;
    int count = 1;

    budgetFile.open("budget.bin", ios::out|ios::binary);
    if(budgetFile.fail())
    {
      cout<<"Could not find file: budget.bin \n";
      system("read");  //act as system("PAUSE")
    }

    try
    {
        do
        {
            cout<<"Enter your housing budget for month "<<count<<":\n";
            cin>>mb.housing;
            cout<<"Enter your utilities budget for month "<<count<<":\n";
            cin>>mb.utilites;
            cout<<"Enter your household expense budget for month "<<count<<":\n";
            cin>>mb.houseHold;
            cout<<"Enter your transportation budget for month "<<count<<":\n";
            cin>>mb.transportation;
            cout<<"Enter your food budget for month "<<count<<":\n";
            cin>>mb.food;
            cout<<"Enter your medical budget for month "<<count<<":\n";
            cin>>mb.medical;
            cout<<"Enter your insurance budget for month "<<count<<":\n";
            cin>>mb.insurance;
            cout<<"Enter your entertainment budget for month "<<count<<":\n";
            cin>>mb.entertainment;
            cout<<"Enter your clothing budget for month "<<count<<":\n";
            cin>>mb.clothinng;
            cout<<"Enter your Miscellaneous budget for month "<<count<<":\n";
            cin>>mb.misc;

            budgetFile.write(reinterpret_cast<char*>(&mb),sizeof(mb));
            count++;
            months --;
        }while(months != 0);
    }

    catch(...)
    {
        cin.clear(); 
        cin.ignore(numeric_limits<streamsize>::max(), '\n');

        cout<<"Error, invalid input entered.";
    }

    budgetFile.close();
}

/**
 * 
 * @param expenseFile
 * @param months
 */
void getMonthlyExpenses(fstream& expenseFile, int months)
{
    MonthlyExpenses me;
    int count = 1;


    expenseFile.open("expenses.bin", ios::out|ios::binary);
    if(expenseFile.fail())
    {
      cout<<"Could not open expenses.bin \n";
      system("read");
    }
    try
    {
        do
        {
            cout<<"Enter your housing expenses for month "<<count<<":\n";
            cin>>me.housingEx;
            cout<<"Enter your utilities expenses for month "<<count<<":\n";
            cin>>me.utilitesEx;
            cout<<"Enter the cost of your house hold expenses for month "<<count<<":\n";
            cin>>me.householdEx;
            cout<<"Enter your transportation expenses for month "<<count<<":\n";
            cin>>me.transportationEx;
            cout<<"Enter your food expenses for month "<<count<<":\n";
            cin>>me.foodEx;
            cout<<"Enter your medical expenses for month "<<count<<":\n";
            cin>>me.medicalEx;
            cout<<"Enter your insurance expenses for month "<<count<<":\n";
            cin>>me.insuranceEx;
            cout<<"Enter your entertainment expenses for month "<<count<<":\n";
            cin>>me.entertainmentEx;
            cout<<"Enter your clothing expenses for month "<<count<<":\n";
            cin>>me.clothinngEx;
            cout<<"Enter your Miscellaneous expenses for month "<<count<<":\n";
            cin>>me.miscEx;    

            expenseFile.write(reinterpret_cast<char*>(&me),sizeof(me));
            count++;    
            months--;   
        }while(months!=0);  
    }

    catch(...)
    {
        cin.clear(); 
        cin.ignore(numeric_limits<streamsize>::max(), '\n');

        cout<<"Error, invalid input entered.";
    } 
    expenseFile.close();
}

/**
 * 
 * @param budgetFile
 * @param expenseFile
 * @param months
 */
void getMonthlyReport(fstream& budgetFile, fstream& expenseFile, int months)
{
    MonthlyBudget mb;
    MonthlyExpenses me;
    ofstream toFile;

    budgetFile.open("budget.bin", ios::in|ios::binary);
    if(budgetFile.fail()){
        cout<<"Could not open budget.bin";
        system("read"); 
    }
    budgetFile.read(reinterpret_cast<char*>(&mb),sizeof(mb));

    expenseFile.open("expenses.bin", ios::in|ios::binary);  
    if(expenseFile.fail()){
        cout<<"Could not open expenses.bin (getMonthlyReport function)";
        system("read");
    }    
    expenseFile.read(reinterpret_cast<char*>(&me),sizeof(me));

    while(  !expenseFile.eof() && !budgetFile.eof()  )
    {
       //displaying info to screen 
       cout<<fixed<<showpoint<<setprecision(2);
       cout<<"--------------------------------------------------------------------\n";
       cout<<left<<setw(14)<<"Category";
       cout<<right<<setw(11)<<"Budget";
       cout<<right<<setw(14)<<"Spent";
       cout<<right<<setw(19)<<"   Over(-)/Under \n";
       cout<<"--------------------------------------------------------------------\n";

       cout<<left<<setw(14)<<"Housing";
       cout<<right<<setw(11)<<mb.housing;
       cout<<right<<setw(14)<<me.housingEx;
       cout<<right<<setw(14)<<(mb.housing - me.housingEx)<<"\n";

       cout<<left<<setw(14)<<"Utilities";
       cout<<right<<setw(11)<<mb.utilites;
       cout<<right<<setw(14)<<me.utilitesEx;
       cout<<right<<setw(14)<<(mb.utilites - me.utilitesEx)<<"\n";

       cout<<left<<setw(14)<<"House Hold";
       cout<<right<<setw(11)<<mb.houseHold;
       cout<<right<<setw(14)<<me.householdEx;
       cout<<right<<setw(14)<<(mb.houseHold - me.householdEx)<<"\n";

       cout<<left<<setw(14)<<"Transportation";
       cout<<right<<setw(11)<<mb.transportation;
       cout<<right<<setw(14)<<me.transportationEx;
       cout<<right<<setw(14)<<(mb.transportation - me.transportationEx)<<"\n";

       cout<<left<<setw(14)<<"Food";
       cout<<right<<setw(11)<<mb.food;
       cout<<right<<setw(14)<<me.foodEx;
       cout<<right<<setw(14)<<(mb.food - me.foodEx)<<"\n";

       cout<<left<<setw(14)<<"Medical";
       cout<<right<<setw(11)<<mb.medical;
       cout<<right<<setw(14)<<me.medicalEx;
       cout<<right<<setw(14)<<(mb.medical - me.medicalEx)<<"\n";

       cout<<left<<setw(14)<<"Insurance";
       cout<<right<<setw(11)<<mb.insurance;
       cout<<right<<setw(14)<<me.insuranceEx;
       cout<<right<<setw(14)<<(mb.insurance - me.insuranceEx)<<"\n";

       cout<<left<<setw(14)<<"Entertainment";
       cout<<right<<setw(11)<<mb.entertainment;
       cout<<right<<setw(14)<<me.entertainmentEx;
       cout<<right<<setw(14)<<(mb.entertainment - me.entertainmentEx)<<"\n";

       cout<<left<<setw(14)<<"Clothing";
       cout<<right<<setw(11)<<mb.clothinng;
       cout<<right<<setw(14)<<me.clothinngEx;
       cout<<right<<setw(14)<<(mb.clothinng - me.clothinngEx)<<"\n";

       cout<<left<<setw(14)<<"Miscellaneous";
       cout<<right<<setw(11)<<mb.misc;
       cout<<right<<setw(14)<<me.miscEx;
       cout<<right<<setw(14)<<(mb.misc - me.miscEx)<<"\n";

       cout<<"For the month you are over budget by $"<<
            (mb.housing + mb.utilites + mb.houseHold + mb.transportation + mb.food +
             mb.medical + mb.insurance + mb.entertainment + mb.clothinng + mb.misc)
             -
            (me.housingEx + me.utilitesEx + me.householdEx + me.transportationEx
             + me.foodEx + me.medicalEx + me.insuranceEx + me.entertainmentEx + 
             me.clothinngEx + me.miscEx )<<"\n";

       //writing to text file
       toFile.open("results.txt",ios::app);
       toFile<<fixed<<showpoint<<setprecision(2);
       toFile<<"--------------------------------------------------------------------\n";
       toFile<<left<<setw(14)<<"Category";
       toFile<<right<<setw(11)<<"Budget";
       toFile<<right<<setw(14)<<"Spent";
       toFile<<right<<setw(19)<<"   Over(-)/Under \n";
       toFile<<"--------------------------------------------------------------------\n";

       toFile<<left<<setw(14)<<"Housing";
       toFile<<right<<setw(11)<<mb.housing;
       toFile<<right<<setw(14)<<me.housingEx;
       toFile<<right<<setw(14)<<(mb.housing - me.housingEx)<<"\n";

       toFile<<left<<setw(14)<<"Utilities";
       toFile<<right<<setw(11)<<mb.utilites;
       toFile<<right<<setw(14)<<me.utilitesEx;
       toFile<<right<<setw(14)<<(mb.utilites - me.utilitesEx)<<"\n";

       toFile<<left<<setw(14)<<"House Hold";
       toFile<<right<<setw(11)<<mb.houseHold;
       toFile<<right<<setw(14)<<me.householdEx;
       toFile<<right<<setw(14)<<(mb.houseHold - me.householdEx)<<"\n";

       toFile<<left<<setw(14)<<"Transportation";
       toFile<<right<<setw(11)<<mb.transportation;
       toFile<<right<<setw(14)<<me.transportationEx;
       toFile<<right<<setw(14)<<(mb.transportation - me.transportationEx)<<"\n";

       toFile<<left<<setw(14)<<"Food";
       toFile<<right<<setw(11)<<mb.food;
       toFile<<right<<setw(14)<<me.foodEx;
       toFile<<right<<setw(14)<<(mb.food - me.foodEx)<<"\n";

       toFile<<left<<setw(14)<<"Medical";
       toFile<<right<<setw(11)<<mb.medical;
       toFile<<right<<setw(14)<<me.medicalEx;
       toFile<<right<<setw(14)<<(mb.medical - me.medicalEx)<<"\n";

       toFile<<left<<setw(14)<<"Insurance";
       toFile<<right<<setw(11)<<mb.insurance;
       toFile<<right<<setw(14)<<me.insuranceEx;
       toFile<<right<<setw(14)<<(mb.insurance - me.insuranceEx)<<"\n";

       toFile<<left<<setw(14)<<"Entertainment";
       toFile<<right<<setw(11)<<mb.entertainment;
       toFile<<right<<setw(14)<<me.entertainmentEx;
       toFile<<right<<setw(14)<<(mb.entertainment - me.entertainmentEx)<<"\n";

       toFile<<left<<setw(14)<<"Clothing";
       toFile<<right<<setw(11)<<mb.clothinng;
       toFile<<right<<setw(14)<<me.clothinngEx;
       toFile<<right<<setw(14)<<(mb.clothinng - me.clothinngEx)<<"\n";

       toFile<<left<<setw(14)<<"Miscellaneous";
       toFile<<right<<setw(11)<<mb.misc;
       toFile<<right<<setw(14)<<me.miscEx;
       toFile<<right<<setw(14)<<(mb.misc - me.miscEx)<<"\n";
       toFile.close();

       expenseFile.read(reinterpret_cast<char*>(&me),sizeof(me));
       budgetFile.read(reinterpret_cast<char*>(&mb),sizeof(mb));       
    }

    budgetFile.close();
    expenseFile.close();
}
\$\endgroup\$
4
  • \$\begingroup\$ As I am not any expert in cpp, I prefer to post short advices instead of full answer: 1) please make a spot that budget and expenses are the same data structures in essence but only have different purpose. 2) instead of using fixed structs of doubles you can use dictionary-like data structure and iterate over its keys. 3) reading budget and expenses for month from the user is in essence the same but under different name 'budget'/'expenses' and into different target variable. \$\endgroup\$
    – mpasko256
    Mar 1, 2018 at 13:31
  • \$\begingroup\$ Does your teacher enforce the rather curious use of system("read")? \$\endgroup\$
    – yuri
    Mar 1, 2018 at 15:11
  • \$\begingroup\$ no, he does however encourage system("PAUSE") which I heard was worse. I just used read because I saw a few forums encouraging that instead. What would be the best option? \$\endgroup\$
    – Zachariah
    Mar 1, 2018 at 16:37
  • \$\begingroup\$ @Zachariah I don't know about the best but cin.get() seems like a better choice. \$\endgroup\$
    – yuri
    Mar 1, 2018 at 16:52

2 Answers 2

2
\$\begingroup\$

DRY you code

DRY: Don't repeat yourself

This piece of code is repeated many times:

   toFile<<left<<setw(14)<<"Insurance";
   toFile<<right<<setw(11)<<mb.insurance;
   toFile<<right<<setw(14)<<me.insuranceEx;
   toFile<<right<<setw(14)<<(mb.insurance - me.insuranceEx)<<"\n";

This means it is ripe for placing into a function:

  void dump(std::ostream& str, std::string const& title, double value, double valueEx) {
      str << left  << setw(14) << title
          << right << setw(11) << value
          << right << setw(14) << valueEx
          << right << setw(14) << (value - valueEx)
          << "\n";
  }

Don't Write your file in BInary

budgetFile.read(reinterpret_cast<char*>(&mb),sizeof(mb));

The trouble here is that this is very brittle. It will work on your current current compiler with this set of compiler flags on the current OS on the current hardware. Any of the these things changes and your data is likely to break.

Rather stream the data (in a human readable form) to a file. That way it can be read portably across multiple systems without much difficulty.

Also you can version your data. So that if in the future you add (or remove) fields into your structure your code can compensate for this.

struct MonthlyBudget
{
    friend std::ostream& operator<<(std::ostream& strm, MonthlyBudget const& mb)
    {
        // Save data to stream.
        str << "2 "    // version 2
            << mb. housing  << " "
            << mb. utilites << " "
            // etc
            << "\n";
        return str;
    }
    friend std::istream& operator>>(std::istream& strm, MonthlyBudget& mb)
    {
        int version;
        str >> version;
        if (1 <= version) {
            // read version 1 fields
            str >> mb. housing
                >> mb. utilites
            // etc
         }
        if (2 <= version) {
            // read version 2 fields
            str >> mb.spaceHop;
        }
        return str;
    }

};

Use the new String literal

Rather than artificially split the text into multiple strings.

cout<<
      "This program will ask you for your desired budget for housing, "
      "utilites, entertainment,  \n etc (8 budget options), and will also ask you for the amount"
      " of money you spent on such expenses. \n First, the program will ask you for how "
      "many months you would like analyzed. These numbers entered will be put into "
      "a \n MonthlyBudget struct and sent to two external files (budget.bin ad expenses.bin). "
      "It will then calculate the difference and display the budget,  \n the expenses, "
      "and over/under in a tabular format.\n";

You can have very long strings over multiple lines now with C++11 string literals. In this case use the RAW string literal.

std:: cout << 
R"(This program will ask you for your desired budget for housing, utilites, entertainment,  etc (8 budget options), and will also ask you for the amount of money you spent on such expenses. 
First, the program will ask you for how many months you would like analyzed. These numbers entered will be put into 
MonthlyBudget struct and sent to two external files (budget.bin ad expenses.bin). It will then calculate the difference and display the budget, and over/under in a tabular format.
)";

Basically the RAW string can contain the newline character naturaly. So it is easier to write and format as you would expect.

// Note the: <optional Marker> must be the same at both ends.
//           <Data> is the actual string used by the compiler
//           and can include all characters you want (or can type).
std::string  str = R"<optional Marker>(<Data>)<optional Marker>";
\$\endgroup\$
1
\$\begingroup\$

Good job breaking up the logic in main() into different functions. However, some improvements can be made.

Avoid using namespace std

This can cause name collisions because it adds every name in the std namespace to the global namespace. For a small program like this one it's unlikely that you'll run into any problems (then again, maybe not) but it's best to get into the habit of using the std:: prefix on names in the std namespace.

Alternatively, you can introduce using declarations like using std::cout; to add specific names to the global namespace.

Make your code more re-useable / Don't Repeat Yourself (DRY)

There are places where you've written a lot of code that does essentially the same thing. For example:

struct MonthlyBudget
{
    double housing;
    double utilites;
    double houseHold;
    double transportation;
    double food;
    double medical;
    double insurance;
    double entertainment;
    double clothinng;
    double misc;
};

struct MonthlyExpenses
{
    double housingEx;
    double utilitesEx;
    double householdEx;
    double transportationEx;
    double foodEx;
    double medicalEx;
    double insuranceEx;
    double entertainmentEx;
    double clothinngEx;
    double miscEx;
};

These two structs hold the same categories of data, just that one holds budget data and the other holds expenses data. You can just create one struct (called, e.g. Categories) and use one variable of that type for all the budget data and another variable of that type for all the expenses data. Using one struct for both reduces the amount of code you have to write and, more importantly, reduces the risk of bugs if you need to add or remove categories: instead of having to remember to modify both of your structs you'd only have to modify the one struct.

Similarly, you have a lot of code like this:

cout<<"Enter your housing budget for month "<<count<<":\n";
cin>>mb.housing;
cout<<"Enter your utilities budget for month "<<count<<":\n";
cin>>mb.utilites;

You can put that into a function that looks like this:

double get_budget(std::string category_name, int count) {
  double budget = 0;
  std::cout << "Enter your " + category_name + " budget for month " << count << ":\n";
  std::cin >> budget;
  // Check for validity of the user input here (e.g. that it is positive).
  return budget;
}

You could do the same with the code that acquires the expenses inputs (in fact, you could write one function for both).

Don't use magic numbers

You've got a lot of repeated code like this:

cout<<left<<setw(14)<<"Housing";
cout<<right<<setw(11)<<mb.housing;
cout<<right<<setw(14)<<me.housingEx;
cout<<right<<setw(14)<<(mb.housing - me.housingEx)<<"\n";

Not only is this an opportunity to refactor that code into a function, but the numbers 14 and 11 (arguments to setw() here) appear multiple times in your code with the same purpose. Instead, define a constant (e.g. const int width = 14) and use that constant. This means that if you ever want to change that value you'd only have to change the value of that constant once (the value of the const int width) instead of trying to find the literal 14 all over your program.

Use meaningful comments

Your functions are commented as so:

/**
 * 
 * @param budgetFile
 * @param expenseFile
 * @param months
 */
void getMonthlyReport(fstream& budgetFile, fstream& expenseFile, int months)
{
  // ...
}

While it's a good idea to provide documentation on the parameters for your functions, these comments as is are not helpful. They just take up space and are distracting -- after all, I can see the names of the parameters just a few lines below the comments.

Instead of simply listing the parameter name, provide some explanation of what values it is allowed to take (e.g. months should be between 1 and 12, inclusive), what happens if the parameter value is not valid, etc.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.