【问题标题】:c++ read txt file and copy into two classes istream::peek()c++读取txt文件并复制到两个类istream::peek()
【发布时间】:2017-05-19 11:07:18
【问题描述】:
istream& operator>>(istream& s, vector<Order>& ord) {

    string c{};
    bool got_customer = false;
    Customer temp_customer{};
    vector<Purchase>vec_purchase{};
    while (s >> c) {
        cout << "Erstes Wort der Zeile: "<<c << endl;

        if (!got_customer) { //c[0] == '#'
            c.erase(c.begin()); //löscht erstes Element vom string
            int k_nummer{};
            string k_vname{};
            string k_nname{};
            string k_plz{};
            string k_stadt{};
            k_nummer = stoi(c); //string to int
            s >> k_vname >> k_nname >> k_plz>>k_stadt;
            k_vname += " " + k_nname;
            temp_customer = Customer(k_nummer, k_vname, k_plz, k_stadt);
            got_customer = true;
        } else {
            string p_name{};
            double p_preis{};
            int p_menge{};
            p_name = c;
            s >> p_preis>>p_menge;
            cout << p_name << "  " << p_preis << "  " << p_menge << endl;
            vec_purchase.push_back(Purchase{p_name, p_preis, p_menge});
        }

        cout<<s.peek()<<endl;
        if (s.peek() == '#') {
            ord.push_back(Order{temp_customer, vec_purchase});
            temp_customer = Customer{};
            vec_purchase.clear();
            got_customer = false;
        }
    }
}

这是txt文件:

#725454 Schenker Rudolf DE-30159 Hannover
Shirt 135.95 1
Tie 89.59 1
#987654 Orbison Roy US-TN37075 Hendersonville
Mug 1.49 3
T-Shirt 14.90 1
#123456 Petty Tom US-FL32641 Gainesville
Flashlight 12.95 2
#246802 Hite Bob US-CA90291 Venice
CannedBeans 0.89 10
CannedTomatoes 1.79 6
#246802 Hite Bob US-CA90291 Venice
CanOpener 0.48 1
Spoon 1.49 4
Needle 0.05 100

以“#”开头的行是客户对象。 其他行是购买对象。

我正在尝试查看即将到来的字符,但我一次又一次地得到相同的字符。在这种情况下,我总是得到 '13' 作为 s.peek() 的输出。

€dit: 谢谢@Mark Ransom。 如果有人有同样的问题,这是工作代码。 :)

istream& operator>>(istream& s, vector<Order>& ord) {
    string c{};
    bool got_customer = false;
    Customer temp_customer{};
    vector<Purchase>vec_purchase{};

    while (s >> c) {
        if (!got_customer) { 
            c.erase(c.begin()); //deletes first char of string
            int k_nummer{};
            string k_vname{};
            string k_nname{};
            string k_plz{};
            string k_stadt{};
            k_nummer = stoi(c); //string to int
            s >> k_vname >> k_nname >> k_plz>>k_stadt>>ws;
            k_vname += " " + k_nname;
            temp_customer = Customer(k_nummer, k_vname, k_plz, k_stadt);
            got_customer = true;
        } else {
            string p_name{};
            double p_preis{};
            int p_menge{};
            p_name = c;
            s >> p_preis>>p_menge>>ws;
            vec_purchase.push_back(Purchase{p_name, p_preis, p_menge});
        }

        if (s.peek() == 35||s.peek() == -1 ) {  //35 (in ascii) -> # , -1 -> end of txt file
            ord.push_back(Order{temp_customer, vec_purchase});
            temp_customer = Customer{};
            vec_purchase.clear();
            got_customer = false;
        }
    }
}

【问题讨论】:

    标签: c++ fstream istream peek


    【解决方案1】:

    当您从流中读取时,输入将在第一个空白字符处停止。十进制代码为 13 的字符是ASCII CR,它位于 Windows 中每一行的末尾;它被认为是空白。您需要跳过空格来查找下一个 重要 字符。您可以使用std::ws 执行此操作。

    s >> k_vname >> k_nname >> k_plz >> k_stadt >> std::ws;
    
    s >> p_preis >> p_menge >> std::ws;
    

    【讨论】:

      【解决方案2】:
       s >> p_preis>>p_menge;
      

      这旨在读取带有记录的行的最后两个字段。紧接着:

       if (s.peek() == '#') {
      

      您的明显意图是查看下一行的第一个字符,检查下一行是否以# 开头。

      好吧,这不会发生,就像那样......

      问题是&gt;&gt; 运算符在处理输入后不消耗行尾字符。 peek() 这里将给你换行符,而不是。惊喜。

      给定一个示例输入行:

       Tie 89.59 1
      

      “1”部分将被&gt;&gt;p_menge 读取,并存储到menge1 之后的下一个字符将是换行符,这就是 peek() 很乐意为您提供的。

      这对于这项任务来说是完全错误的方法。读取包含文本行的文件只有一种正确方法:

      std::getline()

      就是这样。 std::getline(),仅此而已。了解如何使用它。我会让你的生活变得更简单。

      您当然可以使用&gt;&gt; 运算符从输入文件中逐行读取,一次一个字段,但是您发现这是一个容易出错且脆弱的过程。雷区和陷阱太多。

      当然,使用&gt;&gt; 操作符可以做到这一点,但如何做到这一点并不总是很明显,需要完全理解格式化输入操作的所有细微差别。

      不幸的是,太多介绍性的 C++ 书籍一开始就展示了使用 &gt;&gt; 运算符来读取由文本行组成的输入的示例。这只会让大多数初学者感到困惑,并导致诸如此类的问题和误解。

      您需要重写程序以使用std::getline() 一次读取一行文本。故事结束。

      一旦您将整行文本读入 std::string,并检查它是否以 # 字符开头:如果您喜欢它那么 您可以将整行填充到std::istringstream,然后在受控环境中使用&gt;&gt; 运算符。

      【讨论】:

      • 虽然我同意getline 会让生活更轻松,但我不能容忍这种态度,因为它是唯一的答案。
      • 您好,非常感谢您的回答 :) 我当然知道 std::getline 但我必须与 istream_iterators 合作,这是一个学校项目
      • &gt;&gt; 不是 istream 迭代器。此外,如果您慢慢重读我回答中的最后一段,我解释了如何正确使用它。