跳过输入流数据值

5
有没有一种简单的机制可以用 C++ 输入流(如 ifstream)跳过到下一个空格?我知道如果我知道要跳过多少个字符或者期望什么分隔符,可以使用 ignore。但是在我看来,当 operator>> 通常只读取到下一个空格而不提供任何额外参数时,使用 ignore 是不美观的。我也可以使用虚拟变量,但那只会使问题更糟。

示例

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

file >> importantInfo1 >> someDummy >> importantInfo2;

在某些情况下,如果我需要处理“跳过”情况下的不同数据类型,我可能需要多个虚拟变量。

我想象中的解决方法是这样的:

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

也许更好的做法是:
auto importantInfo1 = 0;
auto importantInfo2 = 0;

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

我想这种解决方案的性能会比实际解析并存储从不需要的值时更好。
可能的解决方案
使用提供的答案制作了此解决方案。它基本上与已接受的答案相同,但不需要临时解决方案。相反,它跳过第一个空格,然后跳过除空格以外的任何字符,直到再次到达空格为止。此解决方案可能使用 2 个 while 循环,但无需知道所提取的类型。我并不是说这是一种高性能的解决方案或任何花哨的东西,但它使得结果代码更短、更清晰和更有表现力。
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;
}

@MatthiasBonora 我想帮忙,但我不知道从哪里开始。不过我会尽力想出一些东西。 - Excelcius
@DieterLücking,性能部分只是额外的奖励,我知道它不会对性能造成太大影响。也许int是一个糟糕的例子,它可以是一个字符串,或者是提供流操作符的一些复杂对象。 - Excelcius
scanf 能正常工作吗? - Proxy
std::cin >> std::ws 跳过所有的空格。 - Simple
@Proxy 这个问题更多地涉及到清晰的 C++ 代码,使用 scanf 并不是一个真正的选择。 - Excelcius
显示剩余5条评论
3个回答

2

我认为你的想法,使用操作器跳过数据,是正确的方法。

跳过“琐碎”数据:

#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';
}

如果数据变得更加复杂,构建/流式处理变得昂贵,您需要提供一个专门的重载,并逐个字符解析以跳过它。

运行得很好,对于原始数据类型来说已经足够了。谢谢!也许我会重写跳过函数,使用ignorereadline直接跳过下一个空格而不需要临时变量。 - Excelcius

1

编写自己的忽略规则

#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;
}

这看起来很有前途,但是对于如此简单的目的来说,代码太多了。而且,我自己调用ignore_until并提供isspace并没有真正获得太多好处(尽管我可以轻松地将该检查集成到ignore_until中)。我不害怕使用ignore等函数,我只是想要一种更短、更优雅的方式。 - Excelcius

0
如果你想要像上一个例子中的跳过一样的东西,那么你可以创建一个名为Skip的类,重载operator>>。你需要这样的代码:
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
}

是的,模板参数可能并不必要。我只是想到对于更复杂的数据类型,一些流实现可能需要一些特殊处理。 - Excelcius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接