Reputation: 1690
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.
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.
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
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
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
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