Can I use istream_iterator<char> to copy some istream content into std::string?
Asked Answered
D

1

6

I have an istream and need to copy the content between two delimiters to a std::string. I can find the delimiters' streampos, but when trying to use istream_iterator<char> to iterate over the section of the stream, it does not work. Here's what I tried:

#include <iostream>
#include <sstream>
#include <iterator>


std::string copyToString( std::istream& is )
{
    is >> std::ws;

    auto someLength {10};

    std::istream_iterator<char> beg {is};

    is.seekg( someLength, std::ios::cur );

    //std::istream_iterator<char> end { is };
    std::istream_iterator<char> end { std::next(beg, someLength) };

    return std::string{ beg, end };
}




int main()
{
    std::stringstream ss;
    ss.str( "   { my string content  }" );

    std::cout << "\"" << copyToString( ss ) << "\"\n";

    return 0;
}

I expected the output to be 10 chars long, but it's just "{". If I uncomment the line std::istream_iterator<char> end { is }; and comment out the next one, the output is just "".

What am I missing? Can I not use iterators like that?

I am aware that I could create a char[] as a buffer, copy it to that, etc., but that seems much less straightforward.

December answered 17/10, 2019 at 9:8 Comment(8)
fwiw, when I run your code I get a segfault wandbox.org/permlink/zhPnYCFQtzAn3MiKLayer
std::next(beg, someLength) reads 10 characters from the stream. (However, I don't know how to do this correctly.)Myers
@immibis: I believe you are mistaken: cplusplus.com/reference/iterator/next/?kw=nextDecember
I'm not quite sure about input streams, but aren't you invalidating the beg iterator once you do seekg? I thought the point of these streams is that they do not keep their elements after they are passed?Medin
You could instead do: std::string result(someLength, ' '); is.read(&result[0], someLength); return result;Revoke
@Medin Thought about that, but my guess was that it's istream_iterator that you can't have going backwards, not the istream itself (which can totally do that).December
@Revoke Apart from using resize(), that's precisely what I'm currently doing.December
@December "ForwardIterator shall be at least a forward iterator." - istream_iterator is an input iterator, which is less powerful than a forward iterator, so according to that documentation, using std::next on it is invalid.Myers
C
2

std::istream_iterator is an input iterator. Input iterators are single pass and are invalidated when copies thereof are incremented. When you do std::next(beg, someLength), you read someLength characters. Then beg is invalidated.

Moreover, std::istream_iterator is not intended to be counted. It is designed to be compared to a default constructed iterator, because of the way stream errors are handled. If you attempt count them, then they will sometimes read one more time, depending on how the algorithm is implemented.

If you want to read n characters from an input stream, just use read. If you want to skip spaces, then just write a loop. If you want to use std::istream_iterator in an unintended way, then your code will fail unpredictably.

Clad answered 17/10, 2019 at 9:40 Comment(3)
Thanks. I was not aware that that is an unintended use, as it seems quite basic to me, and an otherwise typical use of iterators.December
@December Yeah, input iterators are quite restricted, to say the least.Clad
But I actually also tried the default constructor std::istream_iterator<char> end;. Why did that not work? Output was "{" IIRC.December

© 2022 - 2024 — McMap. All rights reserved.