使用istream_iterator和istream重载读取到文件末尾

时间:2009-08-30 23:05:05

标签: c++

我在将文件中的数据读入Orders向量时遇到了一些麻烦。

代码:

#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <iterator>

using namespace std;

class Purchase;

class Order {
public:
    string name;
    string address;
    vector<Purchase> items;
};

class Purchase {
public:
    string product_name;
    double unit_price;
    int count;
    Purchase() {}
    Purchase(string pn, double up, int c) :product_name(pn), unit_price(up), count(c) {}
};

istream& operator>>(istream& in, Order& o)
{
    string p_name;
    double u_price;
    int p_count;

    getline(in, o.name);
    getline(in, o.address);
    getline(in, p_name);
    in >> u_price >> p_count;

    o.items.push_back(Purchase(p_name, u_price, p_count));

    return in;
}

ostream& operator<<(ostream& out, const Purchase& p)
{
    out << p.product_name << '\n'
        << p.unit_price << '\n'
        << p.count << '\n';
    return out;
}

ostream& operator<<(ostream& out, const Order& o)
{
        out << '\n' << o.name << '\n'
            << o.address << '\n'
            << o.item << '\n';
        return out;
}

int main()
{
    cout << "Enter file to read orders from: \n";
    string file;
    cin >> file;
    ifstream is(file.c_str());

    istream_iterator<Order> ii(is);
    istream_iterator<Order> eos;
    ostream_iterator<Order> oo(cout);
    vector<Order> orders(ii, eos);

    copy(orders.begin(), orders.end(), oo);
}

我有3个主要问题。

1)当我拿出ostream重载中的o.item错误来测试输出时,它只输出文件中的第一个条目。 txt文件由5行数据组成,这些数据应该被读入向量顺序。

现在txt文件有10个“订单”,但它只将第一个读入订单向量。我可能需要实现某种文件结束操作,但我不知道如何使用istream重载和迭代器来完成此操作。这是最大的问题,如果我能解决这个问题,我想我可能会接下来的两个问题。

2)当问题得到解决时。我将需要处理o.item的输出(当前无法输出的订单中的Purchase的向量,因为没有指定元素)。显然我需要指定要输出的元素,我考虑使用静态int并递增它,但是这需要为每个单独的Order重置,这导致问题3 ...

3)如果读取与先前读取相同的名称/地址,我需要程序理解它是相同的“订单”被读入并简单地将另一个对象添加到该订单的“购买”向量而不是创建新订单。我正在考虑使用find()来检查该名称是否已按顺序存在,在这种情况下,不执行任何名称/地址输入,但如果有更好的方法我想知道。

对不起,如果这有点长。如果需要更多解释,我很乐意详细说明。任何帮助表示赞赏。感谢。

P.S。如果我将o.item输出指定为o.item [0],则此处是输入输出的示例。

文本文件有:

John Smith
117 One Tree Hill
Trampoline
600.00
1 

//... 9 more Orders like this

输出是:

John Smith
117 One Tree Hill
Trampoline
600.00
1
//... Nothing after this....

3 个答案:

答案 0 :(得分:0)

我没有详细查看你的代码,但我会给出一句忠告:

“不要混合格式化和未格式化的输入。事实上,根本不要使用来自文件或用户输入的格式化输入。”

好的,这是两句话。

答案 1 :(得分:0)

关于问题#3,您可以使用多图而不是矢量。

首先,假设您按如下方式拆分Order类:

class Customer{
public:
    string name;
    string address;
};

class Purchase {
public:
    string product_name;
    double unit_price;
    int count;
    Purchase() {}
    Purchase(string pn, double up, int c) :product_name(pn), unit_price(up), count(c) {}
};

class Order {
    Customer c;
    std::vector<Purchase> p;
};

现在您只需创建std::multimap<Customer, Purchase>即可。添加客户/购买对完全符合您的要求:如果客户尚不存在,则添加他,否则只是将购买添加到现有客户。

当然,要实现这一点,您还需要定义一个比较器。最简单的方法可能是为operator <类定义Customer。通过比较名称和忽略地址来实现它。

关于您的其他问题,请避免混合使用getlinestream_iterator。它本身并不是错误的,但它变得非常棘手,因为getline一次读取一行,并且流迭代器只是读取到下一个空格。

老实说,C ++ IOStreams库的使用非常糟糕。由于您的数据格式已经完全按行分隔,我可能只是放弃了流迭代器并在任何地方使用getline

答案 2 :(得分:0)

你遇到的问题非常简单。实际上你的代码很清楚:)

您需要添加的只是那些简单的行:

istream& operator>>(istream& in, Order& o)
{
    string p_name;
    double u_price;
    int p_count;

    getline(in, o.name);
    getline(in, o.address);
    getline(in, p_name);

    in >> u_price >> p_count;

    //
    while(in.peek() == '\n' || in.peek() == ' ')
    {
        in.ignore();
    }
    //

    o.items.push_back(Purchase(p_name, u_price, p_count));

    return in;
}

原因是当使用>>时,它会在newline中留下getline字符。你可以搜索Stackoverflow关于流的一般情况,对这个问题有很多很好的解释。

此外,item中没有任何名为Order的内容。您所拥有的是vector Purchase

ostream& operator<<(ostream& out, const Order& o)
{
        out << '\n' << o.name << '\n'
            << o.address << '\n';
        //
        for(vector<Purchase>::const_iterator i = o.items.begin();
            i != o.items.end(); i++)
        {
            out << *i << '\n';
        }
        //

        return out;
}