12

istreamから読み取る慣用的なループは

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

現在、このループには1つの問題があります。ファイルの終わりが原因でループが終了したのか、エラーが原因でループが終了したのかを区別できません。たとえば、次のテストプログラムを考えてみましょう。

#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は2つの有効なブールが含まれているため、エラーではありません。2番目の呼び出しは3番目の不完全なブール値で終了するため、エラーになります。それにもかかわらず、両方の動作は同じです。前者の場合、ブール値がないために読み取りに失敗し、後者の場合、不完全であるために失敗し、どちらの場合もEOFがヒットします。実際、上記のプログラムは同じ行を2回出力します。

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を返します。

今私の質問:この解決策は機能することが保証されていますか、それとも私はそれがたまたま望ましい結果をもたらしたのは(不幸な)幸運でしたか?また:望ましい結果を得るためのより簡単な(または、私の解決策が間違っている場合は正しい)方法はありますか?

4

1 に答える 1

8

例外を使用するようにストリームを構成しない限り、EOFと他のエラーを区別するのは非常に簡単です。

最後に確認stream.eof()してください。

stream.fail()その前に、またはのように、失敗/非失敗のみをチェックしてください!streamgoodの反対ではないことに注意してくださいfail。したがって、一般的には、を見るgoodだけでなく、を見るだけfailです。


編集:

いくつかのサンプルコード、つまりデータ内の不適切なブール仕様を区別するために変更されたサンプル:

#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:'bool'値の抽出に失敗しました。
--0110
-------------------------------------------------- ----------
T
F
!boolFrom:最後に余分な文字が見つかりました。
--0110

編集2:投稿されたコードと結果に、eof()私が忘れていたチェックの使用例を追加しました。


編集3:次の対応する例では、OPが提案する読み取り前のスキップ空白ソリューションを使用しています。

#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:「bool」値の抽出に失敗しました。
--0110
-------------------------------------------------- ----------
T
F
T
!readbools:「bool」値の抽出に失敗しました。
-0010

主な違いは、このアプローチでは、3番目の値が誤って指定されていても(として"truex")、3番目のケースで3つの正常に読み取られた値が生成されることです。

つまり、誤った仕様をそのように認識できません。

もちろん、機能しないコードを書く私の能力は、それが機能しないという証拠ではありません。しかし、私は物事をコーディングするのはかなり得意であり"truex"、このアプローチでは、が正しくないことを検出する方法を見つけることができませんでした(read-words例外ベースのアプローチで行うのは簡単でしたが)。したがって、少なくとも私にとっては、単語の読み取りの例外ベースのアプローチは、正しく動作させるのが簡単であるという意味で、より単純です。

于 2011-11-12T01:19:38.217 に答える