Example

In this section, we show a second example which involves reading data from a text file, processing the data, and finally displaying the result.

Example 2: Assume that the expenses of the last month have been logged in a text file. The file contains a list of products bought and how much was paid for each product. Note that the same product may appear several times in the file, e.g. pasta which was bough in two different occasions. The aim is to write a program that reads the list of expenses in the text file and displays the following:

  • A list of products bought, sorted alphabetically by products name
  • The total amount spent by product
  • The total amount spent

An example of the text file and the program’s outut is shown below.

Streams

A possible strategy to solve this problem is to read all product expenses in the file into a vector and then sort the vector by products name. When producing the final output, the amount spent for each product needs to be aggregated, as the figure below suggests.

Streams

The C++ program listed below follows this idea. First, the type Product is defined. Two important functions are used to read the text file.

  • Function read_product which reads one product’s data (i.e. name and amount spent) from the text file into a variable of type Product and then returns it. Note that the products name is separated from the amount paid by character '-'. Thus, std::getline(file, p.name, '-'); is used to read a product’s name until the separator character is reached.
  • Function read_file reads all products from the text file and returns a vector with the list of products read. This functions calls function read_product to read separately each product.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <iostream>
#include <iomanip>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>  // std::swap

struct Product {
    std::string name;
    int cost = 0; // total spent on the product
};

/****************************
 * Function declarations    *
 ****************************/

// Compare products by name
bool less_than(const Product& p1, const Product& p2);

Product read_product(std::ifstream& file);

void display_product(const Product& p);

// Read a file of product expenses
// Return a vector with all products read
std::vector<Product> read_file(std::ifstream& file);

// Sort V alphabetically by products name
void sort(std::vector<Product>& V);

void display_aggregated_expenses_list(const std::vector<Product>& V);

/*************************
 *  Main                 *
 *************************/

int main() {
    std::ifstream inFil("expenses_list.txt");

    if (inFil) {  // succeeded to connect the stream inFil to the text file?

        // 1. Read the file and save the products in the expenses_list
        std::vector<Product> expenses_list = read_file(inFil);

        // 2. Sort the expenses list alphabetically
        sort(expenses_list);

        // 3. Display expenses list aggregated by name
        std::cout << "The products\n";

        display_aggregated_expenses_list(expenses_list);
    } else {
        std::cout << "File could not be opened!!\n";
    }
}

/*************************
 *  Function definitions *
 *************************/

// Compare products by name
bool less_than(const Product& p1, const Product& p2) {
    return (p1.name < p2.name);
}

Product read_product(std::ifstream& file) {
    Product p;

    file >> std::ws;                  // skip white spaces before product's name, if any
    std::getline(file, p.name, '-');  // read the product's name until char '-' is found
    file >> p.cost;                   // read product's cost

    return p;
}

void display_product(const Product& p) {
    std::cout << p.name << "- " << p.cost << "\n";
}

// Read all products in file and
// Return a vector with the products read
std::vector<Product> read_file(std::ifstream& file) {
    std::vector<Product> V;

    Product p = read_product(file); // read first product

    while (file) {                  // is stream file in good-state? 
        V.push_back(p);             // save the product in the vector
        
        p = read_product(file);     // read next product
    }
    return V;
}

// Sort V alphabetically by products name
void sort(std::vector<Product>& V) {
   for (int pass = 1; pass < V.size(); ++pass) {
        for (int i = 0; i < V.size() - 1; ++i) {
            if ( less_than(V[i + 1], V[i]) ) {  // wrong order?
                std::swap(V[i], V[i + 1]);
            }
        }
    }
}

// Requirement: vector V is sorted by products' name
// All products with same name appear in consecutive slots of V
void display_aggregated_expenses_list(const std::vector<Product>& V) {
    int index = 0;  // to count slots of V visited
    int total_spent = 0;

    while (index < std::ssize(V)) {
        Product p = V[index];
        int sum = p.cost;

        ++index;  // move to next slot

        // Aggregate: sum the cost for all products named p.name
        while (index < std::ssize(V)            // slot V[index] exists?
               && V[index].name == p.name) {    // V[index] has the same name as the previous product p?
            sum += V[index].cost;
            ++index;  // move to next slot
        }
        total_spent += sum;
        std::cout << std::setw(15) << p.name << ": " << sum << "\n";
    }

    // Display total amount spent
    std::cout << "\n\nTotal spent = " << total_spent << ":-\n";
}

In function display_aggregated_expenses_list, the loop in lines $119$ to $123$ aggregates (i.e. sums) the amount paid for a product p. Note that this loop is not execute for a product p, if p occurs only once in the file.