Getline VS Istream_Iterator

getline vs istream_iterator

I sometimes (depending on the situation) write a line class so I can use istream_iterator:

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

struct Line
{
std::string lineData;

operator std::string() const
{
return lineData;
}
};
std::istream& operator>>(std::istream& str,Line& data)
{
std::getline(str,data.lineData);
return str;
}

int main()
{
std::vector<std::string> lines(std::istream_iterator<Line>(std::cin),
std::istream_iterator<Line>());
}

Reading to end of file with istream_iterator and istream overload

Regarding question #3, you could use a multimap instead of a vector.

First, assume you split your Order class up as follows:

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

Now you can simply create a std::multimap<Customer, Purchase>. Adding a customer/purchase pair does exactly what you want: If the customer doesn't already exist, he is added, otherwise the purchase is just added to the existing customer.

Of course, for this to work, you need to define a comparer as well. Simplest way might just be to define operator < for the Customer class. Implement it by comparing the name and disregarding the address.

As for your other questions, avoid mixing getline and stream_iterators. It's not wrong per se, but it gets pretty tricky because getline reads a line at a time, and stream iterators just read to the next whitespace.

Honestly, the C++ IOStreams library is pretty awful to use in general. Since your data format is already cleanly line-separated already, I'd probably just ditch the stream iterators and use getline everywhere.

Distance between istream_iterators

std::istream_iterator is an InputIterator, which means it only supports a single pass over the range you're iterating. There's no way to figure out the size, and then go back to the beginning to read the data.

You can read from std::cin (or any other input stream) using std::copy without knowing the size beforehand, just use std::back_inserter to append the data being read to the vector.

std::vector v;
std::copy( std::istream_iterator<std::string>(std::cin),
std::istream_iterator<std::string>(),
std::back_inserter(v) );

Is there a C++ iterator that can iterate over a file line by line?

EDIT: This same trick was already posted by someone else in a previous thread.

It is easy to have std::istream_iterator do what you want:

namespace detail 
{
class Line : std::string
{
friend std::istream & operator>>(std::istream & is, Line & line)
{
return std::getline(is, line);
}
};
}

template<class OutIt>
void read_lines(std::istream& is, OutIt dest)
{
typedef std::istream_iterator<detail::Line> InIt;
std::copy(InIt(is), InIt(), dest);
}

int main()
{
std::vector<std::string> v;
read_lines(std::cin, std::back_inserter(v));

return 0;
}


Related Topics



Leave a reply



Submit