从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。
现在我的问题是:这个解决方案是否可以保证有效,或者我只是(非)幸运的是它碰巧得到了理想的结果?另外:是否有更简单的(或者,如果我的解决方案是错误的,正确的)获得所需结果的方法?
答案 0 :(得分:7)
只要您不将流配置为使用例外,就可以很容易地区分EOF和其他错误。
最后只需检查stream.eof()
。
在此之前,只检查失败/非失败,例如stream.fail()
或!stream
。请注意,good
与fail
不同。因此,一般情况下,只能查看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。,