Excelcius
Excelcius

Reputation: 1690

Skipping input stream values

Is there any easy mechanism to skip until the next whitespace with a C++ input stream (like ifstream)?

I know that I can use ignore if I know how many characters to skip or what delimiter to expect. But IMO it's ugly to use ignore when operator>> usually just reads to the next whitespace without supplying any additional parameters. I could also use a dummy, but that only makes matters worse.

Example

auto importantInfo1 = 0;
auto importantInfo2 = 0;
auto someDummy = 0; // This is ugly and doesn't clearly express the intent

file >> importantInfo1 >> someDummy >> importantInfo2;

Also in some cases I will need more than one dummy if I need to handle different datatypes in the "skip"-case.

I would imagine something like this:

file >> importantInfo1;
file.skip<int>(1);
file >> importantInfo2;

Or maybe even better:

auto importantInfo1 = 0;
auto importantInfo2 = 0;

file >> importantInfo1 >> skip<int> >> importantInfo2;

I imagine such a solution would also perform better than actually parsing and storing the value somewhere when it is never needed.

Possible solution

Made this solution using the answers provided. It's basically the same as the accepted answer, but it doesn't need a temporary. Instead, it skips the first whitespaces, then skips any character except whitespaces until whitespace is reached again. This solution may use 2 while loops but doesn't need to know about the extracted type. I'm not saying this is a high performance solution or anything fancy, but it makes the resulting code shorter, cleaner and more expressive.

template<typename CharT, typename Traits>
inline std::basic_istream<CharT, Traits>& skip(std::basic_istream<CharT, Traits>& stream)
{
    while (stream && std::isspace(stream.peek())) stream.ignore();
    while (stream && !std::isspace(stream.peek())) stream.ignore();
    return stream;
}

Upvotes: 5

Views: 3924

Answers (3)

user2249683
user2249683

Reputation:

I think your idea having a manipulator to skip data is the right way to go.

Skip 'trivial' data:

#include <sstream>

template<typename T, typename Char, typename Traits>
inline std::basic_istream<Char, Traits>& skip(std::basic_istream<Char, Traits>& stream) {
    T unused;
    return stream >> unused;
}

int main()
{
    std::istringstream in("1 666 2 ");
    int a;
    int b;
    in >> a >> skip<int> >> b;
    std::cout << a << b << '\n';
}

If the data gets more complex and construction/streaming becomes expensive you have to provide an specialized overload and parse char by char to skip it.

Upvotes: 2

cpp-progger
cpp-progger

Reputation: 406

write Your own ignore

#include <locale>
#include <iostream>

template< typename E, typename Traits, typename Pred >
std::basic_istream< E, Traits >& ignore_until( std::basic_istream< E, Traits >& in, Pred end )
{
    std::basic_istream< E, Traits >::sentry ok( in );
    if( ok )
    {
        std::ios_base::iostate state = std::ios_base::goodbit;
        try
        {
            for( Traits::int_type m = in.rdbuf()->sgetc(); ; m = in.rdbuf()->snextc() )
            {
                if( Traits::eq_int_type( m, Traits::eof() ) )
                {
                    state |= std::ios_base::eofbit;
                    break;
                }
                const E c = Traits::to_char_type( m );
                if( end( c ) )
                    break;
            }
        }
        catch( ... )
        {
            state |= std::ios_base::badbit;
            if( in.exceptions() & std::ios_base::badbit )
                throw;
        }
        in.setstate( state );
    }
    return in;
}

int main()
{
    using namespace std;
    locale loc = cin.getloc();
    for( int i; ignore_until( cin, [&loc]( char c ) { return std::isspace(c,loc); } ) >> i; )
        cout << i << endl;
}

Upvotes: 1

lthreed
lthreed

Reputation: 444

If you wanted something like the skip in the last example then you could create a class Skip that overloads operator>>. You would need something like:

class Skip {
    friend istream& operator>> (istream &in, Skip &skip);
};

istream& operator>> (istream &in, Skip &skip) {
    // I'm not sure why you are parameterizing skip, but this is where
    // you would do whatever handles that
}

Upvotes: 0

Related Questions