区分读循环中的失败和文件结束

时间:2011-11-11 22:35:13

标签: c++ error-handling istream

从istream中读取的惯用循环是

while (thestream >> value)
{
  // do something with value
}

现在这个循环有一个问题:它不能区分循环是由于文件结束还是由于错误而终止。例如,参加以下测试程序:

#include <iostream>
#include <sstream>

void readbools(std::istream& is)
{
  bool b;
  while (is >> b)
  {
    std::cout << (b ? "T" : "F");
  }
  std::cout << " - " << is.good() << is.eof() << is.fail() << is.bad() << "\n";
}

void testread(std::string s)
{
  std::istringstream is(s);
  is >> std::boolalpha;
  readbools(is);
}

int main()
{
  testread("true false");
  testread("true false tr");
}

testread的第一次调用包含两个有效的bool,因此不是错误。第二个呼叫以第三个不完整的bool结束,因此是一个错误。然而,两者的行为是相同的。在第一种情况下,读取布尔值失败,因为没有,而在第二种情况下,它失败,因为它是不完整的,并且在两种情况下都会命中EOF。实际上,上面的程序输出两次相同的行:

TF - 0110
TF - 0110

为了解决这个问题,我想到了以下解决方案:

while (thestream >> std::ws && !thestream.eof() && thestream >> value)
{
  // do something with value
}

这个想法是在实际尝试提取值之前检测常规EOF。因为文件末尾可能有空格(这不是错误,但导致读取最后一项没有命中EOF),我首先丢弃任何空格(不能失败),然后测试EOF。只有当我不在文件的末尾时,我才会尝试读取该值。

对于我的示例程序,它确实似乎有效,我得到了

TF - 0100
TF - 0110

因此,在第一种情况下(正确输入),fail()返回false。

现在我的问题是:这个解决方案是否可以保证有效,或者我只是(非)幸运的是它碰巧得到了理想的结果?另外:是否有更简单的(或者,如果我的解决方案是错误的,正确的)获得所需结果的方法?

1 个答案:

答案 0 :(得分:7)

只要您不将流配置为使用例外,就可以很容易地区分EOF和其他错误。

最后只需检查stream.eof()

在此之前,只检查失败/非失败,例如stream.fail()!stream。请注意,goodfail不同。因此,一般情况下,只能查看good,而不是fail


修改

一些示例代码,即您修改的示例,用于区分数据中的ungood bool规范:

#include <iostream>
#include <sstream>
#include <string>
#include <stdexcept>
using namespace std;

bool throwX( string const& s )  { throw runtime_error( s ); }
bool hopefully( bool v )        { return v; }

bool boolFrom( string const& s )
{
    istringstream stream( s );
    (stream >> boolalpha)
        || throwX( "boolFrom: failed to set boolalpha mode." );

    bool result;
    (stream >> result)
        || throwX( "boolFrom: failed to extract 'bool' value." );

    char c;  stream >> c;
    hopefully( stream.eof() )
        || throwX( "boolFrom: found extra characters at end." );

    return result;
}

void readbools( istream& is )
{
    string word;
    while( is >> word )
    {
        try
        {
            bool const b = boolFrom( word );
            cout << (b ? "T" : "F") << endl;
        }
        catch( exception const& x )
        {
            cerr << "!" << x.what() << endl;
        }
    }
    cout << "- " << is.good() << is.eof() << is.fail() << is.bad() << "\n";
}

void testread( string const& s )
{
    istringstream is( s );
    readbools( is );
}

int main()
{
  cout << string( 60, '-' ) << endl;
  testread( "true false" );

  cout << string( 60, '-' ) << endl;
  testread( "true false tr" );

  cout << string( 60, '-' ) << endl;
  testread( "true false truex" );
}

示例结果:

------------------------------------------------------------
T
F
- 0110
------------------------------------------------------------
T
F
!boolFrom: failed to extract 'bool' value.
- 0110
------------------------------------------------------------
T
F
!boolFrom: found extra characters at end.
- 0110

编辑2 :在发布的代码和结果中,添加了使用eof()检查的示例,我忘记了。


编辑3 : 以下相应示例使用OP提出的skip-whitespace-before-reading解决方案:

#include <iostream>
#include <sstream>
#include <string>
using namespace std;

void readbools( istream& is )
{
    bool b;
    while( is >> ws && !is.eof() && is >> b )       // <- Proposed scheme.
    {
        cout << (b ? "T" : "F") << endl;
    }
    if( is.fail() )
    {
        cerr << "!readbools: failed to extract 'bool' value." << endl;
    }
    cout << "- " << is.good() << is.eof() << is.fail() << is.bad() << "\n";
}

void testread( string const& s )
{
    istringstream is( s );
    is >> boolalpha;
    readbools( is );
}

int main()
{
  cout << string( 60, '-' ) << endl;
  testread( "true false" );

  cout << string( 60, '-' ) << endl;
  testread( "true false tr" );

  cout << string( 60, '-' ) << endl;
  testread( "true false truex" );
}

示例结果:

------------------------------------------------------------
T
F
- 0100
------------------------------------------------------------
T
F
!readbools: failed to extract 'bool' value.
- 0110
------------------------------------------------------------
T
F
T
!readbools: failed to extract 'bool' value.
- 0010

主要区别在于,即使第三个值未正确指定("truex"),此方法也会在第三种情况下生成3个成功读取值。

即。它无法识别错误的规范。

当然,我编写“不起作用的代码”的能力并不能证明它无法正常工作。但我相当擅长编写代码,并且我无法通过这种方法看到任何方法将"truex"检测为不正确(虽然使用基于读取单词异常的方法很容易)。所以至少对我来说,基于读取词异常的方法更简单,从某种意义上说,它很容易让它表现得正常。

干杯&amp;第h。,