Expense Tracker Project Overview
The Expense Tracker is a C++ application designed to help users manage their personal finances efficiently. It allows them to input, view, and categorize their expenses. The project provides features such as file handling, data storage, and formatting to make expense management simple and user-friendly.
Key Features
1. Expense Recording
Users can add their expenses to the tracker by providing the following details:
- Category: This helps in categorizing expenses (e.g., “Food”, “Entertainment”, “Transport”). Categorization is important for budget analysis.
- Amount: The actual amount spent on the particular expense. The program accepts it as a double to ensure that fractional amounts (like 10.75 for a meal) are handled correctly.
- Date: The date when the expense occurred. This is input in the format YYYY-MM-DD to maintain consistency and easy sorting. It is represented as a string to ensure flexibility in parsing and handling.
void addExpense(const string &cat, double amt, const string &dt) { expenses.emplace_back(cat, amt, dt); cout << "Expense added successfully!\n"; }
Expected Output:
Enter category: Food Enter amount: 10.75 Enter date (YYYY-MM-DD): 2025-01-01 Expense added successfully!
The addExpense method in the ExpenseTracker class adds a new expense to the tracker. This method creates an instance of the Expense class and stores it in the expenses vector.
2. Persistent Storage
One of the key features of this application is data persistence:
- Saving Data: The data entered by the user is saved in a text file (extenses.txt) using the ofstream class. Each expense is written on a new line in the format category,amount,date
- Loading Data: When the application starts, it loads the previously saved expenses from exception.txt using the ifstream class. This allows users to keep track of their past expenses even after the program is closed and reopened.
void saveToFile() { ofstream file("expenses.txt"); for (const auto &expense : expenses) { file << expense.category << "," << expense.amount << "," << expense.date << "\n"; } file.close(); } void loadFromFile() { ifstream file("expenses.txt"); if (!file.is_open()) return; string line; while (getline(file, line)) { size_t pos1 = line.find(","); size_t pos2 = line.find_last_of(","); if (pos1 != string::npos && pos2 != string::npos && pos1 != pos2) { string cat = line.substr(0, pos1); double amt = stod(line.substr(pos1 + 1, pos2 - pos1 - 1)); string dt = line.substr(pos2 + 1); expenses.emplace_back(cat, amt, dt); } } file.close(); }
Expected Output:
1. Add Expense 2. View All Expenses 3. View Expenses by Category 4. Exit Enter your choice: 2 Category Amount Date ---------------------------------------- Food 10.75 2025-01-01 Transport 50.00 2025-01-02
By using file handling, users don’t have to worry about losing their data after closing the application, and it ensures that the program behaves consistently even across multiple sessions.
3. Viewing Expenses
3.1 View All Expenses
The view Expensesmethod in the ExpenseTracker class displays all the expenses recorded in the system:
- It checks whether any expenses are recorded.
- If there are expenses, it formats them into a neat table with columns for category, amount, and date.
- If no expenses are recorded, it informs the user that there are no records.
The expenses are printed in a formatted table using the setw function from <iomainp> to align the text for easy readability.
void viewExpenses() const { if (expenses.empty()) { cout << "No expenses recorded.\n"; return; } cout << setw(15) << "Category" << setw(10) << "Amount" << setw(15) << "Date" << "\n"; cout << string(40, '-') << "\n"; for (const auto &expense : expenses) { cout << setw(15) << expense.category << setw(10) << expense.amount << setw(15) << expense.date << "\n"; } }
Expected Output:
1. Add Expense 2. View All Expenses 3. View Expenses by Category 4. Exit Enter your choice: 2 Category Amount Date ---------------------------------------- Food 10.75 2025-01-01 Transport 50.00 2025-01-02
3.2 View Expenses by Category
The viewByCategory method allows users to view expenses that belong to a specific category. Users input the category (e.g., “Food”), and the program filters expenses by that category:
- It iterates through the expenses vector and displays each expense of the specified category.
- It also keeps a running total of the expenses in that category, so users can quickly see how much they’ve spent.
- If no expenses match the category, the method notifies the user.
void viewByCategory(const string &cat) const { double total = 0; cout << "Expenses for category: " << cat << "\n"; for (const auto &expense : expenses) { if (expense.category == cat) { cout << setw(10) << expense.amount << " on " << expense.date << "\n"; total += expense.amount; } } cout << "Total: " << total << "\n"; }
Expected Output:
1. Add Expense 2. View All Expenses 3. View Expenses by Category 4. Exit Enter your choice: 3 Enter category: Food 10.75 on 2025-01-01 Total: 10.75
4. File Handling
File handling is crucial for storing and retrieving data in this application:
- Saving Data: The program writes each expense as a comma-separated string to expenses.txt. The file is opened in output mode (ofstrem), and after writing all the data, the file is closed.
- Loading Data: Upon program startup, the program opens expenses.txt using ifstrem, reads each line, and parses it into individual expense components. These components are then used to create Expense objects, which are added to the expenses vector.
void saveToFile() { ofstream file("expenses.txt"); for (const auto &expense : expenses) { file << expense.category << "," << expense.amount << "," << expense.date << "\n"; } file.close(); } void loadFromFile() { ifstream file("expenses.txt"); if (!file.is_open()) return; string line; while (getline(file, line)) { size_t pos1 = line.find(","); size_t pos2 = line.find_last_of(","); if (pos1 != string::npos && pos2 != string::npos && pos1 != pos2) { string cat = line.substr(0, pos1); double amt = stod(line.substr(pos1 + 1, pos2 - pos1 - 1)); string dt = line.substr(pos2 + 1); expenses.emplace_back(cat, amt, dt); } } file.close(); }
By using a text file, this project provides a simple yet effective way to store and retrieve data.
5. Object-Oriented Design
The application is built using Object-Oriented Programming (OOP) principles:
- Class: This class represents a single expense entry. It contains attributes for the category, amount, and date of the expense. The class has a constructor that initializes these attributes, making it easier to create and manage individual expenses.
- ExpenseTracker Class: This class manages a collection of Expense objects. It provides functionality for adding, viewing, saving, and loading expenses. It acts as the main controller for the application, handling user input and interaction.
class Expense { public: string category; double amount; string date; Expense(const string &cat, double amt, const string &dt) { category = cat; amount = amt; date = dt; } };
By using classes and objects, the code is organized, modular, and easy to extend.
6. User Interface (Console-Based)
The user interface is console-based and is driven by a simple text menu:
- The user is presented with options to:
- Add an expense
- View all expenses
- View expenses by category
- Exit the program
- Based on user input, the program calls the appropriate methods in the expenseTracker class. This makes the application interactive and easy to use.
int main() { ExpenseTracker tracker; int choice; do { cout << "\nExpense Tracker\n"; cout << "1. Add Expense\n"; cout << "2. View All Expenses\n"; cout << "3. View Expenses by Category\n"; cout << "4. Exit\n"; cout << "Enter your choice: "; cin >> choice; switch (choice) { case 1: { string cat, dt; double amt; cout << "Enter category: "; cin >> cat; cout << "Enter amount: "; cin >> amt; cout << "Enter date (YYYY-MM-DD): "; cin >> dt; tracker.addExpense(cat, amt, dt); break; } case 2: tracker.viewExpenses(); break; case 3: { string cat; cout << "Enter category: "; cin >> cat; tracker.viewByCategory(cat); break; } case 4: cout << "Exiting Expense Tracker.\n"; break; default: cout << "Invalid choice. Please try again.\n"; } } while (choice != 4); return 0; }
Expected Output:
1. Add Expense 2. View All Expenses 3. View Expenses by Category 4. Exit Enter your choice: 4 Exiting Expense Tracker.
The use of a simple console interface ensures that the application remains lightweight and user-friendly without requiring complex graphical user interfaces (GUIs).
7. Memory Management
The program uses the C++ vector container to store expenses. vector is a dynamic array that grows as needed, and it automatically handles memory allocation and deallocation. When new expenses are added using emplace_back, the vector expands to accommodate the new element.
Additionally, the use of RAII (Resource Acquisition Is Initialization) ensures that file resources are properly managed. The ofstrem and ifstreaam objects are automatically closed when they go out of scope.
8. Error Handling
Error handling is implemented to ensure smooth program execution:
- File Opening: If the expenses.txt file cannot be opened (e.g., due to file permission issues or missing file), the program gracefully handles this scenario by returning early in the loadFromFile() method.
- Invalid Input: While the program doesn’t explicitly handle invalid inputs for categories and amounts (such as strings when numbers are expected), it can be enhanced to do so using input validation and exception handling techniques.
9. Extensibility
The project is designed to be easily extended. Some potential future improvements include:
- Input Validation: Adding checks for user input to ensure that amounts are valid numbers and dates are correctly formatted.
- Advanced Features: Allowing users to edit or delete expenses, or adding more complex features like monthly reports, charts, or expense forecasting.
- GUI Interface: Adding a graphical user interface (GUI) using a framework like Qt or GTK to make the application more user-friendly.
- Data Export: Adding functionality to export expense data in CSV or JSON formats for easier sharing or further analysis.
Program Flow
- User Interaction: The program runs inside a do-while loop, where the user is prompted to select an option from the menu:
- Option 1: Add an expense
- Option 2: View all expenses
- Option 3: View expenses by category
- Option 4: Exit the application
- Data Persistence: Upon exiting, the program saves all expenses to a file (Expenses.txt) When the program starts again, it reads from the file and loads the data into the application.
- Dynamic Updates: Users can continuously add new expenses, view them, or filter by categories. The data is updated in real-time, providing an up-to-date view of the expenses.
Code Structure
- Expense Class:
- Represents an individual expense with attributes (category,amount,date).
- Constructor to initialize the object with values.
- ExpenseTracker Class:
- Contains a vector<Expense>to store all the expenses.
- Methods to add, view, save, and load expenses from the file.
- Handles user input/output.