ifstream 的 eof() 是如何工作的?

How does ifstream's eof() work?

提问人:roxrook 提问时间:12/26/2010 最后编辑:jwwroxrook 更新时间:6/3/2017 访问量:192157

问:

#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;
}

我对功能真的很困惑。假设我的ex.txt的内容是:eof()

abc

它总是读取一个额外的字符,并在阅读时显示。但是给出了正确的输出,即“abc”?谁能帮我解释一下?-1eof()inf >> c

C++ IF流 EOF

评论

0赞 roxrook 12/26/2010
谢谢你指出这一点。我在 TopCoder 上竞争,为了节省时间,我通常会导入所有命名空间。

答:

11赞 Justin Spahr-Summers 12/26/2010 #1

EOF 标志仅在读取操作尝试读取文件末尾后设置。 返回符号常量(恰好等于 -1),因为它到达了文件的末尾并且无法读取更多数据,并且只有在那时才为 true。如果要检查此条件,可以执行如下操作:get()traits::eof()eof()

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

评论

7赞 Thanatos 12/26/2010
“EOF 标志仅在读取操作到达文件末尾后设置”正是导致 EOF 混淆的语句。EOF 标志仅在读取操作尝试读取文件末尾后设置。最后一点很关键:如果我从 3 个字节长的文件中读取 3 个字节,则 EOF 为 false,直到我尝试再次读取。
0赞 Ken Bloom 12/26/2010
iostream 返回 EOF 常量。返回值在文件末尾似乎是未定义的 -- 您必须检查int istream::get()istream::eof()
0赞 Thanatos 12/26/2010
@Ken Bloom:你确定吗?我的标准是“退货:如果有的话,否则。27.6.1.3(草案 2,96 年 12 月 2 日)ctraits::eof()
1赞 Ken Bloom 12/26/2010
<s>你是对的,但这与 C stdio.<s 使用的常量相去甚远>我错了。在它的定义中,有某处说返回EOFchar_traits<char>char_traits<char>::eof()EOF
1赞 Justin Spahr-Summers 12/26/2010
@Martin York EOF 标志可能未设置,但仍会返回 ,如果读取失败,则用作一般错误代码。get()traits::eof()
8赞 Ken Bloom 12/26/2010 #2

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;
  }

一个更好的习惯用语是将读取移动到循环条件中,如下所示: (您可以使用所有返回的读取操作(包括运算符)执行此操作)istream*this>>

  char c;
  while(is.get(c))
    cout << c;

评论

0赞 Thanatos 12/26/2010
应该“ 读取操作 ...包括操作员“改为”......包括操作员“?istream<<>>
0赞 Ken Bloom 12/27/2010
@Thanatos:是的。这就是我在深夜发帖所得到的。
2赞 binki 3/12/2018
你想要而不是.例如,当从文件中读取一个数字时,流必须到达文件的末尾,然后才能确定它是否读取数字的最后一位数字。在这种情况下,将成功读取一个数字,但将到达文件末尾,从而导致返回 false。这可能会导致您跳过文件上的最后一个值。相反,在所有情况下,检查都会做正确的事情。!is.fail()is.good()is.good()!is.fail()
0赞 SMGreenfield 7/9/2019
@KenBloom - 如果 while(is.get(c)) 中的流是二进制的,如果字节的值为 0(二进制文件的完全预期值),这是否会过早终止循环?将 while(n=is.get() != Traits::eof()) 用于二进制文件不是更好吗?
2赞 Ken Bloom 7/11/2019
@smgreenfield:获取输出参数的 Get 重载返回 iStream& 以进一步链接或错误报告。
2赞 zeuxcg 12/26/2010 #3

eof() 检查流状态下的 eofbit。

在每次读取操作中,如果位置位于流的末尾,并且必须读取更多数据,则 eofbit 设置为 true。因此,在获得 eofbit=1 之前,您将获得一个额外的字符。

正确的方法是在读取操作检查是否达到了 eof(或者读取操作是否成功)。这就是第二个版本的作用 - 执行读取操作,然后使用生成的流对象引用(>>返回)作为布尔值,这会导致检查 fail()。

评论

0赞 Martin York 12/26/2010
它实际上检查了几个位。
86赞 Thanatos 12/26/2010 #4

-1 表示您已到达文件末尾。使用 (or ) 进行比较 - 避免 -1,这是一个神奇的数字。(虽然另一个有点啰嗦 - 你总是可以打电话getstd::char_traits<char>::eof()std::istream::traits_type::eof()istream::eof)

EOF 标志仅在读取尝试读取文件末尾时设置。如果我有一个 3 字节的文件,而我只读取 3 个字节,则 EOF 是 ,因为我还没有尝试读取文件末尾。虽然这对于通常知道其大小的文件来说似乎令人困惑,但在某些设备(例如管道和网络套接字)上尝试读取之前,EOF 是未知的。false

第二个例子的工作方式是总是返回,其副作用是尝试读取某些内容并将其存储在 中。,在 或 中,将计算文件是否为“良好”:无错误,无 EOF。因此,当读取失败时,会导出到 ,并且循环会正确中止。但是,请遵循以下常见错误:inf >> fooinffooinfifwhiletrueinffalse

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.
}

这就是我们想要的。

评论

3赞 Martin York 12/26/2010
可能不应该使用 >> 而不是 get()。请记住,>>运算符在尝试读取下一个值之前总是跳过继续空格,而 get() 一次读取一个字符。
0赞 chosentorture 7/12/2013
@Thanatos:很抱歉打扰你,但是......流呢?您说当我们尝试读取文件末尾的字符时设置了 EOF,但是当我们尝试输入流 cin 没有字符时查看 () 时会发生什么?标准说它被调用,但当我尝试以这种方式使用它时,peek() 似乎在等待输入traits::eof()
0赞 Thanatos 8/24/2013
peek如果您尝试窥视输入的末尾,将返回 EOF。但是,通常是您键入的终端。(这似乎是你的情况。“等待用户输入”与EOF不同,它只是数据源(您)很慢。所以块,等着你给它输入。如果输入 EOF(在 Linux 上为 Ctrl+D),则将返回,并且应为 true。否则,它将返回您键入的内容。cinpeekpeekeof
0赞 Zesen Qian 12/25/2015
我有一个问题。文档说 bool 运算符定义为 !fail(),所以 EOF 流应该被解释为 true?为什么它会返回 false?
0赞 Zesen Qian 12/25/2015
问题解决了。在上面给出的 failed() 链接中,Reaching the End-of-File sets the eofbit. But note that operations that reach the End-of-File may also set the failbit if this makes them fail (thus setting both eofbit and failbit).