Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructing a vector with istream_iterators

I recall once seeing a clever way of using iterators to read an entire binary file into a vector. It looked something like this:

#include <fstream>
#include <ios>
#include <iostream>
#include <vector>

using namespace std;

int main() {
    ifstream source("myfile.dat", ios::in | ios::binary);
    vector<char> data(istream_iterator(source), ???);
    // do stuff with data
    return 0;
}

The idea is to use vector's iterator range constructor by passing input iterators that specify the entire stream. The problem is I'm not sure what to pass for the end iterator.

How do you create an istream_iterator for the end of a file? Am I completely misremembering this idiom?

like image 305
Adrian McCarthy Avatar asked Dec 12 '10 18:12

Adrian McCarthy


People also ask

How do you add a vector to a constructor?

Begin Declare a class named as vector. Declare vec of vector type. Declare a constructor of vector class. Pass a vector object v as a parameter to the constructor.

How do you traverse a vector?

Use an iterator An iterator can be generated to traverse through a vector. vector<int>::iterator iter; An iterator is used as a pointer to iterate through a sequence such as a string or vector . The pointer can then be incremented to access the next element in the sequence.

How does Istream iterator work?

istream_iterator has a special state end of stream iterator which is acquired when the end of the stream is reached or when an input operation fails. The end of stream iterator is returned by the default constructor.

What is Istreambuf_iterator?

std::istreambuf_iterator is a single-pass input iterator that reads successive characters from the std::basic_streambuf object for which it was constructed. The default-constructed std::istreambuf_iterator is known as the end-of-stream iterator.


2 Answers

You want the std::istreambuf_iterator<>, for raw input. The std::istream_iterator<> is for formatted input. As for the end of the file, use the stream iterator's default constructor.

std::ifstream source("myfile.dat", std::ios::binary);
std::vector<char> data((std::istreambuf_iterator<char>(source)),
                       std::istreambuf_iterator<char>());

Edited to satisfy C++'s most vexing parse. Thanks, @UncleBens.

like image 109
wilhelmtell Avatar answered Sep 18 '22 13:09

wilhelmtell


In C++11 one could:

std::ifstream source("myfile.dat", std::ios::binary);
std::vector<char> data(std::istreambuf_iterator<char>(source), {});

This shorter form avoids the most vexing parse problem because of the {} argument, which removes ambiguity of it being an argument or a formal parameter.

@wilhelmtell's answer could also be updated to avoid this problem by adopting a brace initializer for data. Still in my view, using {} is more simple and turn the initialization form irrelevant.

EDIT

Or, if we had std::lvalue (and maybe std::xvalue instead of std::move):

#include <vector>
#include <fstream>

template <typename T>
constexpr T &lvalue(T &&r) noexcept { return r; }

int main() {
    using namespace std;

    vector<char> data(
        istreambuf_iterator<char>(lvalue(ifstream("myfile.dat", ios::binary))),
        {}
    );
}
like image 28
pepper_chico Avatar answered Sep 21 '22 13:09

pepper_chico