次のプログラムは、std::istream (具体的には私のテスト コードでは std::istringstream) が eof() を設定する方法の矛盾を示しています。
#include <sstream>
#include <cassert>
int main(int argc, const char * argv[])
{
// EXHIBIT A:
{
// An empty stream doesn't recognize that it's empty...
std::istringstream stream( "" );
assert( !stream.eof() ); // (Not yet EOF. Maybe should be.)
// ...until I read from it:
const int c = stream.get();
assert( c < 0 ); // (We received garbage.)
assert( stream.eof() ); // (Now we're EOF.)
}
// THE MORAL: EOF only happens when actually attempting to read PAST the end of the stream.
// EXHIBIT B:
{
// A stream that still has data beyond the current read position...
std::istringstream stream( "c" );
assert( !stream.eof() ); // (Clearly not yet EOF.)
// ... clearly isn't eof(). But when I read the last character...
const int c = stream.get();
assert( c == 'c' ); // (We received something legit.)
assert( !stream.eof() ); // (But we're already EOF?! THIS ASSERT FAILS.)
}
// THE MORAL: EOF happens when reading the character BEFORE the end of the stream.
// Conclusion: MADNESS.
return 0;
}
そのため、実際のファイルの終わりの前に文字を読み取ると、 eof() が「起動」します。ただし、ストリームが空の場合、実際に文字を読み取ろうとしたときにのみ発生します。eof() は「最後まで読み込もうとした」という意味ですか? または「もう一度読もうとすると、最後から外れますか?」答えは矛盾しています。
さらに、アサートが発火するかどうかはコンパイラに依存します。たとえば、Apple Clang 4.1 はアサーションを起動します (前の文字を読み取るときに eof() を発生させます)。たとえば、GCC 4.7.2 はそうではありません。
この矛盾により、ストリームを読み通すが、空のストリームと空でないストリームの両方を適切に処理する賢明なループを作成することが困難になります。
オプション1:
while( stream && !stream.eof() )
{
const int c = stream.get(); // BUG: Wrong if stream was empty before the loop.
// ...
}
オプション 2:
while( stream )
{
const int c = stream.get();
if( stream.eof() )
{
// BUG: Wrong when c in fact got the last character of the stream.
break;
}
// ...
}
では、ストリームを解析し、各文字を順番に処理し、すべての文字を処理するループを作成するにはどうすればよいでしょうか。しかし、EOF に到達したとき、または最初からストリームが空の場合に大騒ぎせずに停止します。 、始まらない?
わかりました、より深い質問: peek() を使用すると、この eof() の不一致を何らかの方法で回避できるかもしれないという直感がありますが、...なんてこった! 矛盾の理由は?