IndieProgrammer
IndieProgrammer

Reputation: 587

istream_iterator behaviour

I have two pieces of code.They work properly when it is used alone in the main().

vector<int> v;

cout << "Enter sequance of integers "<< "(press q to quit) : ";
istream_iterator<int> start_cin(cin);
istream_iterator<int> end_of_cin;
copy(start_cin,end_of_cin,back_inserter(v));

for ( vector<int>::iterator It = v.begin();It != v.end(); It++ )
    cout << *It << "\t";
cout << endl;

and

vector<string> vS;
cout << "Enter three strings : ";
for ( int i = 0; i < 3; i++ )
    vS.push_back(*istream_iterator<string>(cin));

ostream_iterator<string> sIt(cout,", ");
copy(vS.begin(),vS.end(),sIt);
cout << endl;

When these two part use together,i.e

#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
#include <string>
using namespace std;

int main ()
{
    // first part

    vector<int> v;

    cout << "Enter sequance of integers "<< "(press q to quit) : ";
    istream_iterator<int> start_cin(cin);
    istream_iterator<int> end_of_cin;
    copy(start_cin,end_of_cin,back_inserter(v));

    for ( vector<int>::iterator It = v.begin();It != v.end(); It++ )
        cout << *It << " \t";
    cout << endl;


    vector<string> vS;
    cout << "Enter three strings : ";
    for ( int i = 0; i < 3; i++ )
        vS.push_back(*istream_iterator<string>(cin));

    ostream_iterator<string> sIt(cout,", ");
    copy(vS.begin(),vS.end(),sIt);

    cout << endl;

    return 0;
}

here first part worked but second part give output: Enter Three Strings : , , ,. I want to know that what is the reason behind this behaviour?

Thanks.

Upvotes: 0

Views: 1190

Answers (2)

hmjd
hmjd

Reputation: 122001

After the copy() has completed cin will be in an unreadable state (!cin.good()), due to the failed read of the "integer" q. This means the subsequent for loop will fail to read anything.

Add:

cin.clear();
cin.ignore(); // To skip the unread "q"

before the for loop.

EDIT:

As commented by James Kanze, check to ensure "q" was the cause of the termination of the copy():

...

cin.clear();
string int_read_terminator;
cin >> int_read_terminator;
if ("q" != int_read_terminator)
{
    cerr << "Integer copy() failure: " << int_read_terminator << "\n";
}
else
{
    ...

Upvotes: 3

James Kanze
James Kanze

Reputation: 154027

You've just encountered one of the problems with input_iterator: it requires the entire file to be of one type. There are several ways of working around this; the most general is to insert a filtering streambuf between the actual source and the stream. Thus, for example, the first part of your stream should terminate when you enter a single line with just a 'q', something like:

class UntilQStreambuf : public std::streambuf
{
    std::streambuf*     mySource;
    char                myBuffer;
    bool                myIsAtStartOfLine;

protected:
    int underflow()
    {
        int                 results = mySource->sbumpc();
        if ( results == 'q' 
                && myIsAtStartOfLine
                && mySource->sgetc() == '\n' ) {
            mySource->sbumpc();     //  remove terminator line.
            results = traits_type::eof();
        }
        if ( results != traits_type::eof() ) {
            myBuffer = results;
            setg( &myBuffer, &myBuffer, &myBuffer + 1 );
        }
        return results;
    }

public:
    UntilQStreambuf( std::istream& source )
        :  mySource( source->rdbuf() )
        ,  myIsAtStartOfLine( true )
    {
    }
};

(I think boost::iostream has some support which would make this significantly simpler to write.) You then set up a separate stream for reading the numbers, using the streambuf from std::cin (or whereever):

std::vector<int>
getNumbers( std::istream& source )
{
    UntilQStreambuf     localSB( source );
    std::istream        src( &localSB );
    std::vector<int>    results( (std::istream_iterator<int>( src )),
                                 (std::istream_iterator<int>()) );
    if ( !src.eof() ) {
        //  Some other error occurred...
    }
    return results;
}

By using a separate stream, the end condition won't be set in the original stream, and you can continue with it later (perhaps using more of the same technique).

Upvotes: 0

Related Questions