How to read arbitrary number of values using std::copy?

旧城冷巷雨未停 提交于 2019-12-19 00:56:00

问题


I'm trying to code opposite action to this:

std::ostream outs; // properly initialized of course
std::set<int> my_set; // ditto

outs << my_set.size();
std::copy( my_set.begin(), my_set.end(), std::ostream_iterator<int>( outs ) );

it should be something like this:

std::istream ins;

std::set<int>::size_type size;
ins >> size;

std::copy( std::istream_iterator<int>( ins ), std::istream_iterator<int>( ins ) ???, std::inserter( my_set, my_set.end() ) );

But I'm stuck with the 'end' iterator -- input interators can't use std::advance and neither I can use two streams with the same source...

Is there any elegant way how to solve this? Of course I can use for loop, but maybe there's something nicer :)


回答1:


You could derive from the istream_iterator<T>.
Though using Daemin generator method is another option, though I would generate directly into the set rather than use an intermediate vector.

#include <set>
#include <iterator>
#include <algorithm>
#include <iostream>


template<typename T>
struct CountIter: public std::istream_iterator<T>
{
    CountIter(size_t c)
        :std::istream_iterator<T>()
        ,count(c)
    {}
    CountIter(std::istream& str)
        :std::istream_iterator<T>(str)
        ,count(0)
    {}

    bool operator!=(CountIter const& rhs) const
    {
        return (count != rhs.count) && (dynamic_cast<std::istream_iterator<T> const&>(*this) != rhs);
    }
    T operator*()
    {
        ++count;
        return std::istream_iterator<T>::operator*();
    }

    private:
        size_t  count;
};

int main()
{
    std::set<int>       x;

    //std::copy(std::istream_iterator<int>(std::cin),std::istream_iterator<int>(),std::inserter(x,x.end()));
    std::copy(
                CountIter<int>(std::cin),
                CountIter<int>(5),
                std::inserter(x,x.end())
            );
}



回答2:


Use:

std::copy( std::istream_iterator<int>(ins),
           std::istream_iterator<int>(),
           std::inserter(my_set, my_set.end())
         );

Note the empty parameter:

std::istream_iterator<int>();



回答3:


Errr... copy_n() algorithm?




回答4:


Looking into this a bit I don't think reading directly into a set will work, as you need to call insert on it to actually add the elements (I could be mistaken, it is rather early in the morning here). Though looking at the STL documentation in VS2005 briefly I think something using the generate_n function should work, for instance:

std::istream ins;
std::set<int> my_set;
std::vector<int> my_vec;

struct read_functor
{
    read_functor(std::istream& stream) :
        m_stream(stream)
    {
    }

    int operator()
    {
        int temp;
        m_stream >> temp;
        return temp;
    }
private:
    std::istream& m_stream;
};

std::set<int>::size_type size;
ins >> size;
my_vec.reserve(size);

std::generate_n(my_vec.begin(), size, read_functor(ins));
my_set.insert(my_vec.begin(), my_vec.end());

Hopefully that's either solved your problem, or convinced you that the loop isn't that bad in the grand scheme of things.




回答5:


How about using an alternate iterator to do the traversal and then use a function object (or lambda) to fill in the container?

istream ins;
set<int>::size_type size;
set<int> new_set;
ins >> size;
ostream_iterator<int> ins_iter(ins);

for_each(counting_iterator<int>(0), counting_iterator<int>(size),
  [&new_set, &ins_iter](int n) { new_set.insert(*ins_iter++); }
);

Of course this assumes you have a C++0x compliant compiler.

BTW, 'counting_iterator<>' is part of Boost.Iterator.




回答6:


Or you could do this:

my_set.insert(std::istream_iterator<int>(ins), std::istream_iterator<int>());



回答7:


Yes sdg but when I want to use another data structures in that file / stream? I should probably explicitly write here, I want to store another stuff after this set, this is the reason why I'm storing the size as well.




回答8:


(Edited: I should have read the question closer...)

While somewhat suspect, you can get approximately the right behavior by having an entry in the file that will "fail" the first loop, then clear the fail bit on the stream and start reading more.

Data, without an explicit size, but like this

1 1 2 3 5 8 Fibb

Fed to the code below seems to do what I meant, at least on VS2005 with STLPort.

typedef std::istream_iterator < int, char, std::char_traits ,ptrdiff_t> is_iter;
std::copy( is_iter(cin), is_iter(), inserter(my_set,my_set.end()));
cin.clear();
std::cin >> instr;


来源:https://stackoverflow.com/questions/250096/how-to-read-arbitrary-number-of-values-using-stdcopy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!