web-dev-qa-db-ja.com

ifstreamのeof()はどのように機能しますか?

_#include <iostream>
#include <fstream>

int main() {
    std::fstream inf( "ex.txt", std::ios::in );
    while( !inf.eof() ) {
        std::cout << inf.get() << "\n";
    }
    inf.close();
    inf.clear();
    inf.open( "ex.txt", std::ios::in );
    char c;
    while( inf >> c ) {
        std::cout << c << "\n";
    }
    return 0;
}
_

eof()関数について本当に混乱しています。私のex.txtのコンテンツが次のようだったとします:

_abc
_

eof()を使用して読み取る場合、常に余分な文字を読み取り、_-1_を表示します。しかし、_inf >> c_は 'abc'であった正しい出力を与えましたか?誰でも私にこれを説明するのを手伝ってもらえますか?

40
Chan

-1はgetのファイルの終わりに達したという方法です。 std::char_traits<char>::eof()(またはstd::istream::traits_type::eof())を使用して比較します--1を避けます。これはマジックナンバーです。 (もう一方は少し冗長ですが、いつでもistream::eofを呼び出すことができます)

EOFフラグは、読み取りが読み取りを試行した場合にのみ設定されますファイルの最後)。3バイトのファイルがあり、3バイトしか読み取れない場合、 EOFはfalseです。これは、まだファイルの終わりを超えて読み取ろうとしていないためです。これは、通常サイズを知っているファイルにとっては混乱しているようですが、EOFは、パイプやネットワークソケットなどの一部のデバイスで読み取りが試行されるまで不明です。

2番目の例は、inf >> fooが常にinfを返すように機能しますが、何かを読み取ってfooに保存しようとする副作用があります。 infまたはif内のwhileは、ファイルが「良好」である場合、エラーなし、EOFなしの場合、trueと評価されます。したがって、読み取りが失敗すると、inffalseに評価され、ループは適切に中止されます。ただし、次の一般的なエラーに注意してください。

while(!inf.eof())  // EOF is false here
{
    inf >> x;      // read fails, EOF becomes true, x is not set
    // use x       // we use x, despite our read failing.
}

ただし、これ:

while(inf >> x)  // Attempt read into x, return false if it fails
{
    // will only be entered if read succeeded.
}

それが私たちが望むものです。

69
Thanatos

EOFフラグは、読み取り操作がファイルの終わりを超えて読み取ろうとした後にのみ設定されます。get()はシンボリック定数traits::eof()(これはファイルの最後に到達し、それ以上データを読み取ることができず、その時点でのみeof()が真になるため、たまたま-1と等しくなります。この状態を確認する場合は、次のことができます。次のようなことをしてください:

int ch;
while ((ch = inf.get()) != EOF) {
    std::cout << static_cast<char>(ch) << "\n";
}

iostreamは、ファイルの終わりを超えて最初の文字を読み取ろうとするまで、ファイルの終わりにあることを認識しません。

cplusplus.comのサンプルコード は、次のように実行することを示しています(ただし、実際にこの方法で実行しないでください)。

  while (is.good())     // loop while extraction from file is possible
  {
    c = is.get();       // get character from file
    if (is.good())
      cout << c;
  }

より良いイディオムは、次のように読み取りをループ条件に移動することです:allistream読み取り操作return *this、 含んでいる >>演算子)

  char c;
  while(is.get(c))
    cout << c;
7
Ken Bloom

eof()は、ストリーム状態のeofbitをチェックします。

各読み取り操作で、位置がストリームの終わりにあり、さらにデータを読み取る必要がある場合、eofbitはtrueに設定されます。したがって、eofbit = 1を取得する前に余分な文字を取得します。

正しい方法は、eofに到達したかどうか(または、読み取り操作が成功したかどうか)after読み取り操作を確認することです。これが2番目のバージョンの動作です。読み取り操作を実行し、結果のストリームオブジェクト参照(>>が返す)をブール値として使用し、fail()をチェックします。

2
zeuxcg