Alien_SM
Alien_SM

Reputation: 145

Reading a partial file stream into a string using iterators

This is what I have tried so far but with no success:

std::string ReadPartial( std::ifstream& _file, int _size )
{
    std::istreambuf_iterator<char> first( _file );
    std::istreambuf_iterator<char> last( _file );
    std::advance( last, _size );
    return std::string( first, last ); 
}

I know how to read the whole file.

std::string Read( std::ifstream& _file )
{
    std::istreambuf_iterator<char> first( _file );
    std::istreambuf_iterator<char> last();
    return std::string( first, last ); 
}

But this is not what i want to do. I'm getting an empty string. If I look at first and last in a debugger they point to the same thing even after the std::advance.

Upvotes: 8

Views: 3106

Answers (3)

Marcelo Cantos
Marcelo Cantos

Reputation: 185902

Is there some particular reason you want to use iterators? You could just read the bytes in one go:

std::string s(_size, '\0');
_file.read(&s[0], _size);

If you really want to read using iterators, you could do this:

std::string ReadPartial( std::ifstream& _file, int _size )
{
    std::istreambuf_iterator<char> first( _file );
    std::istreambuf_iterator<char> last;
    std::string s;
    s.reserve(_size);
    while (_size-- && first != last) s += *first++;
    return s;
}

Upvotes: 6

Roger Pate
Roger Pate

Reputation:

std::istreambuf_iterator<char> first( _file );
std::istreambuf_iterator<char> last( _file );
std::advance( last, _size );

istreambuf_iterators are Input Iterators. Once you advance last, the other iterator is modified too. You are treating them as Forward Iterators, which have the property that you can copy an iterator, advance it, then get an identical sequence by advancing the copy.

For the general case:

template<class InIter, class Size, class OutIter>
void copy_n(InIter begin, InIter end, Size n, OutIter dest) {
  for (; begin != end && n > 0; ++begin, --n) {
    *dest++ = *begin;
  }
}

//...
std::string ReadPartial(std::istream& file, int size) {
  std::string result;
  copy_n(istreambuf_iterator<char>(file), istreambuf_iterator<char>(),
         size, back_inserter(result));
  return result;
}

However, in this case, you would be better off resizing the string, using istream::read directly into &result[0], and finally checking that you read the desired number of characters.

Upvotes: 6

Bart van Ingen Schenau
Bart van Ingen Schenau

Reputation: 15768

There is no standard algorithm that can help you here, but you can use this one:

template< class InputIterator, class OutputIterator>
OutputIterator copy_n(InputIterator from,
                      size_t n,
                      OutputIterator to)
{
    while (n)
    {
        *to = *from;
        ++from;
        ++to;
        --n;
    }
    return to;
}

This can be used with ReadPartial like this:

std::string ReadPartial( std::ifstream& _file, int _size )
{
    std::istreambuf_iterator<char> first( _file );
    std::string result;

    copy_n(first, _size, std::back_inserter(result));
    return result; 
}

Upvotes: 1

Related Questions