2

I was reading Constructing a vector with istream_iterators which is about reading a complete file contents into a vector of chars. While I want a portion of a file to be loaded in to a vector of chars.

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

using namespace std;

int main(int argc, char *argv[])
{
    ifstream ifs(argv[1], ios::binary);
    istreambuf_iterator<char> beginItr(ifs);
    istreambuf_iterator<char> endItr(beginItr);
    advance(endItr, 4);
    vector<char> data(beginItr, endItr);
    for_each(data.cbegin(), data.cend(), [](char ch)
    {
            cout << ch << endl;
    });
}

This doesn't work, since advance doesn't work, while the aforementioned question's accepted answer works. Why doesn't the advance work on istreambuf_iterator?

Also

  endItr++;
  endItr++;
  endItr++;
  endItr++;
  cout << distance(beginItr, endItr) << endl;

returns a 0. Please somebody explain what is going on!

Community
  • 1
  • 1
legends2k
  • 31,634
  • 25
  • 118
  • 222
  • `istream_iterator`s are input iterators, aka single-pass. Read up, you're completely misusing them. Specifically `endItr(beginItr);` will cause `endItr` and `beginItr` to consume *the same stream*. – Xeo Sep 03 '12 at 10:15
  • @Xeo: So you mean, one cannot have 2 iterators to the same ifstream at all because input_iterators behave that way? – legends2k Sep 03 '12 at 11:56
  • _2 iterators to the same ifstream_ share the same underlying `std::basic_streambuf` and are, therefore, the same. An _end_ `std::istreambuf_iterator` iterator is default-constructed. – bit2shift Nov 05 '18 at 15:22
  • Additionally, `std::distance(begin, end)` affects the underlying `std::basic_streambuf` by incrementing `begin` until `begin == end`. – bit2shift Nov 05 '18 at 15:24
  • @bit2shift Thanks for clarifying. – legends2k Nov 06 '18 at 08:44

3 Answers3

10

Why doesn't the advance work on istreambuf_iterator?

It works. It advances the iterator. The problem is that an istreambuf_iterator is an input iterator but not a forward iterator, meaning it is a single pass iterator: once you advance it, you can never access the previous state again. To do what you want you can simply use an old-fashioned for loop that counts to 4.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • So reading it directly into a vector via vector's ctor is ruled out? – legends2k Sep 03 '12 at 11:45
  • But the problem I've is with multiple iterators, I understand that once `endItr` is advanced it cannot be moved backward, but does that mean all iterators are the same? Or like I've asked to Xeo that an ifstream can have only one iterator (conceptually) at all times? – legends2k Sep 03 '12 at 11:57
  • @legends2k yes, that's it. It stems from the fact that streams in general are not seekable (this of network streams for example). I'm not sure if advancing `endItr` invalidates `beginItr` or if they become the same, but either way, what you're trying doesn't work. I guess there could be a specific iterator for files that allowed multi-pass iteration, but there isn't :( You could also write an iterator that wraps the istreambuf_iterators and counts the number of advances; that would allow you to use the vector constructor directly. – R. Martinho Fernandes Sep 03 '12 at 12:03
  • The **problem** is `endItr` being initialised with `beginItr`, effectively sharing the same `std::basic_streambuf` object. `endItr` needs to be default-constructed to make it an *end-of-stream* iterator. Always read the [documentation](https://en.cppreference.com/w/cpp/iterator/istreambuf_iterator) when in doubt. – bit2shift Nov 05 '18 at 14:25
2

istreambuf_iterator reads successive bytes from a basic_streambuf. It does not support positioning (basic_istream::seekg and basic_istream::tellg).

This is because it is a single-pass input iterator, not a forward iterator or a random-access iterator; it is designed to be able to work with streams that do not support positioning (e.g. pipes or TCP sockets).

You may find some of the solutions in bidirectional iterator over file/ifstream useful.

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
0

If anyone else stumbles across this problem, and wants some alternative simple code you can try something like this instead:

        std::vector<char> buffer{4};
        std::ifstream source{argv[1], std::ios_base::binary};
        source.read(buffer.data(), buffer.size());
bit2shift
  • 656
  • 1
  • 9
  • 17
Pellet
  • 2,254
  • 1
  • 28
  • 20