Robby
Robby

Reputation: 207

How replace specific value from textfile using ofstream c++?

Hi I am working an a vending machine and I want to update the quantity of the item by updating the text file. I been trying using ofstream and ifstream but is not working.

This is my text file.

Water:1:1.99:D1
Coke:4:2.79:D2
Milk:6:3.15:D3
Ham Sandwitch:9:4.50:L1
Lunchables:3:6.00:L2
Cereal:2:3.59:L3
M&M:8:1.75:C1
SourPatch:0:2.10:C2
Twix:6:2.99:C3

This is where vending machine checks the user input and where i want to update the file

void vendingWorking(Item &item) {

if(item.quantity == 0) {
  cout << endl;
  cout << "------------------------------------" << "\n";
  cout << "" << item.name << " (OutStock)" << endl;
  cout << "------------------------------------" << "\n";
} 
else {  
  //Check if itemCode is same as product ID
  if(itemCode == item.productId) {

//HERE I WANT TO UPDATE THE QUANTITY OF THE ITEM IF USER HAS PICKED ONE
//EXAMPLE: (Old) Water:2:2.50:D1 -> (New) Water:1:2.50:D1

      //Message for user
      cout << endl;
      cout << "------------------------------------" << "\n";
      cout << "" << item.name << ", $" << fixed << setprecision(2)<< item.price << " (InStock)" << "\n" ;
      //Pass value to vector
      tempBasket.push_back({item.name, item.price});
  }
}

}

Upvotes: 1

Views: 325

Answers (1)

A M
A M

Reputation: 15277

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;
}

Upvotes: 1

Related Questions