简体   繁体   English

如何使用 ofstream C++ 替换文本文件中的特定值?

[英]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.我一直在尝试使用 ofstream 和 ifstream 但不起作用。

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

} }

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.我在这里回答了一个问题,它描述了 4 种不同的分割线的方法。 In the below given example, I use a std::regex based solution using std::regex_match .在下面给出的示例中,我使用基于std::regex的解决方案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.在这段代码中,我使用了 C++17 特性,比如if with initializer。 So, If you want to compile, then please enable C++17 for your compiler.所以,如果你想编译,那么请为你的编译器启用 C++17。

Additionally, this is just some code to illustrate the explanations above.此外,这只是一些代码来说明上述解释。 There are 1 million solutions.有100万个解决方案。 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM