What you would like to do is:
- Read the product content of the vending machine from a file
- Modify data somehow
- Write the product content of the vending machine to a file
How does modify somehow work? As you cannot change files online with arbitrary new data, you need to do like this:
Read file into memory --> operate on data in memory --> Save modified data in file
For the above there are 2 approaches.
- Open file --> Read data --> Close file --> Modfiy data in memory --> Open file for output by overwrite original file --> Save Data --> Close file
Or, a little bit safer:
- Open file --> Read data --> Close file --> Modfiy data in memory --> Open temporary file for output --> Save Data in temporary file --> Close temporary file --> If everything OK, delete original file --> rename temporary file to orignial file name
But the key is, to work on the data in memory.
You can also create "load" and "save" fucntions. So, at any time, after changing data in memory, you could "save" the modified data. With one of the above described methods.
Or, you could "load" your data in a constructor and "save" it in a destructor. Everything would then work automatically.
Regarding the "load" function. You need to read the source file line by line and then split the line into your needed data members. I have answered a question here, which describes 4 different methods on how to split a line. In the below given example, I use a std::regex
based solution using std::regex_match
. This will ensure that the data is in the expected format.
Please note that you should also overwrite the extractor and inserter operators >>
and <<
for easier working with streams.
And last but not least, everything should be encapsulated in classes.
Please see a working and tested example code for a partial implemented vending machine functionality. In this code I am using C++17 features, like if
with initializer. So, If you want to compile, then please enable C++17 for your compiler.
Additionally, this is just some code to illustrate the explanations above. There are 1 million solutions. In the end you need to come up with sometthing fitting the requirements.
#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
#include <vector>
#include <regex>
#include <algorithm>
#include <numeric>
const std::regex re{ R"(^([^:]+):(\d+):(\d+\.\d+):([A-Z]+\d+))" };
class VendingMachine {
// Local definition of item struct
struct Item {
// Item attributes
std::string name{};
unsigned long quantity{};
double price{};
std::string productID{};
// Simple overwrite of extractor operator
friend std::istream& operator >> (std::istream& is, Item& it) {
// Read a complete line and check, if that worked
if (std::string line{}; std::getline(is, line)) {
// Check, if the input line, is in the expected format
if (std::smatch sm{}; std::regex_match(line, sm, re)) {
it.name = sm[1];
it.quantity = std::stoul(sm[2]);
it.price = std::stod(sm[3]);
it.productID = sm[4];
}
else std::cerr << "\n***Error while reading: '" << line << "'\n'";
}
return is;
}
// Simple overwrite of inserter operator
friend std::ostream& operator << (std::ostream& os, const Item& it) {
return os << it.name << ':' << it.quantity << ':' << it.price << ':' << it.productID;
}
};
// All products in vending machine
std::vector<Item> products{};
// Filename for saving and loading
std::string fileName{ "products.txt" };
public:
// Constructor and Destructor
// Constructor will load the data from a file
VendingMachine() { load(); }; // Default constructor
VendingMachine(const std::string& fn) : fileName(fn) { load(); }; // Constructor + file name
// Destructor will automatically save product file
~VendingMachine() { save(); };
// Simple overwrite of extractor operator
friend std::istream& operator >> (std::istream& is, VendingMachine& vm) {
// Delete all existing products
vm.products.clear();
// Copy all data from stream into internal structure
std::copy(std::istream_iterator<Item>(is), {}, std::back_inserter(vm.products));
return is;
}
// Simple overwrite of extractor operator
friend std::ostream& operator << (std::ostream& os, const VendingMachine& vm) {
// Copy all data to stream
std::copy(vm.products.begin(), vm.products.end(), std::ostream_iterator<Item>(os, "\n"));
return os;
}
// Load file from file
void load() {
// Open file and check, if it could be opened
if (std::ifstream ifs(fileName); ifs) {
// Use existing extractor operator
ifs >> *this;
}
else std::cerr << "\n***Error: Could not open file '" << fileName << "' for reading\n";
}
// Save products to file
void save() {
// Open file and check, if it could be opened
if (std::ofstream ofs(fileName); ofs) {
// Use existing inserter operator
ofs << *this;
}
else std::cerr << "\n***Error: Could not open file '" << fileName << "' for writing\n";
}
// Show the complete content of the vending machine. Even if one product category quantity is 0
void displayContent() {
// Some header line
std::cout << "\nNumber of selections in vending machine: " << products.size() << "\n\nProducts:\n\n";
// All Items wit their attributes
for (const Item& item : products)
std::cout << item.productID << "\t Quantity: " << item.quantity << "\t Price: " << item.price << "\t --> " << item.name << '\n';
}
// Select an item and the decrease quatnity
void getItem() {
// COunt the number of overall items in the vending maschine
const unsigned long overallItemQuantity = std::accumulate(products.begin(), products.end(), 0UL, [](size_t sum, const Item& it) {return sum + it.quantity; });
// If there are at all products in the machine and not all item quantity is 0
if (products.size() && overallItemQuantity > 0UL ) {
// Instruction from user
std::cout << "\n\nGet item\nPlease select from below list:\n\n";
// Show list of possible selections
for (const Item& item : products) {
if (item.quantity > 0UL) std::cout << item.productID << " \tPrice " << item.price << " \t--> " << item.name << '\n';
}
// Get user input. What item does the user want to have
std::cout << "\n\nPlease select product by typing the ID: ";
if (std::string id{}; std::getline(std::cin, id)) {
// FInd the selected item in the product list
if (std::vector<Item>::iterator iter{ std::find_if(products.begin(), products.end(),[&id](const Item& i) {return i.productID == id && i.quantity > 0UL; }) };iter != products.end())
// In my example I do not handle payment. Simply decrease quantity
--iter->quantity;
else
std::cerr << "\n\n***Error: Unknown product ID\n"; // Wrong input
}
}
else std::cerr << "\n\n***Error: Vending machine empty\n";
}
// Run the machine. Main menu and actions. At the moment kust get items without payment
// Needs to be extended for real application
void run() {
// We run the main menu in a loop as long as the machine is active
bool active{ true };
while (active) {
// Show main menu
std::cout << "\n\n\nMain menu. Please select:\n 1 --> Get Item\n 0 --> Exit\n\nOption: ";
// Get user selection
unsigned int option; std::cin >> option;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Depending on the user selected action
switch (option) {
case 0:
// Leave function.
active = false;
std::cout << "\n\nExiting . . .\n";
break;
case 1:
// Get an item
std::cout << "\n";
getItem();
break;
default:
std::cout << "\n\n\nError: Wrong selection. Please try again\n";
break;
}
}
}
};
int main() {
// Define a Vending Machine. Read data from disk
VendingMachine vendingMachine;
// SHow what is in initially
vendingMachine.displayContent();
// Run the machine
vendingMachine.run();
// Show, what is now in the machine
vendingMachine.displayContent();
// Destructor of vendingMachine will be called and file automatically saved
return 0;
}