【问题标题】:How does ifstream's eof() work?ifstream 的 eof() 是如何工作的?
【发布时间】:2011-05-30 19:17:09
【问题描述】:
#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 &gt;&gt; c 给出了正确的输出,即“abc”?谁能帮我解释一下?

【问题讨论】:

  • 感谢您指出这一点。我在 TopCoder 上竞争,我通常会导入所有命名空间以节省时间。

标签: c++ ifstream eof


【解决方案1】:

-1 是get 表示您已到达文件末尾的方式。使用std::char_traits&lt;char&gt;::eof()(或std::istream::traits_type::eof())比较它——避免-1,这是一个神奇的数字。 (虽然另一个有点冗长 - 你可以随时打电话给istream::eof

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

第二个示例的工作原理是inf &gt;&gt; foo 将始终返回inf,其副作用是尝试读取某些内容并将其存储在foo 中。 inf,在 ifwhile 中,如果文件是“好”,则评估为 true:没有错误,没有 EOF。因此,当读取失败时,inf 评估为false,并且您的循环正确中止。但是,请采取以下常见错误:

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

这是我们想要的。

【讨论】:

  • 谢谢塔纳托斯。我想我现在明白了;)
  • 可能不应该使用 >> 代替 get()。请记住,>> 运算符总是在尝试读取下一个值之前跳过继续进行的空格,而 get() 一次读取每个字符。
  • @Thanatos:很抱歉打扰你,但是......流呢?您说当我们尝试读取文件末尾之后的字符时设置了 EOF,但是 当我们在输入流 cin 中尝试 peek() 时会发生什么情况? 标准说traits::eof() 被调用,但是当我尝试以这种方式使用它时,peek() 似乎在等待输入
  • peek 将返回 EOF 如果您试图窥视输入的末尾。但是,cin 通常是您正在输入的终端。 (这似乎是你的情况。)“等待用户输入”与 EOF 不同,它只是数据源(你)很慢。所以peek 阻塞,等待你给它输入。如果输入 EOF(Linux 上为 Ctrl+D),则 peek 将返回,eof 应为 true。否则,它会返回您输入的内容。
  • 我有一个问题。文档说bool operator 被定义为!fail(),所以EOFed 流应该被解释为true?怎么会返回false?
【解决方案2】:

只有在读取操作尝试读取文件末尾之后才会设置 EOF 标志。 get() 正在返回符号常量 traits::eof()(恰好等于 -1),因为它到达了文件末尾并且无法读取更多数据,并且只有在这一点上 eof() 才会为真。如果要检查这种情况,可以执行以下操作:

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

【讨论】:

  • "只有在读取操作到达文件末尾后才会设置 EOF 标志。"正是导致 EOF 混淆的语句。 EOF 标志仅在读取操作尝试读取文件末尾之后 设置。最后一点很关键:如果我从 3 个字节长的文件中读取 3 个字节,EOF 为假,直到我尝试再次读取。
  • iostream 返回 EOF 常量。 int istream::get() 的返回值在文件末尾似乎未定义——您必须检查 istream::eof()
  • @Ken Bloom:你确定吗?我的标准是“返回:c 如果可用,否则 traits::eof()。” 27.6.1.3(草案 2,96 年 12 月 2 日)
  • 你是对的,但这与 C stdio 使用的 EOF 常量相差甚远。我错了。在char_traits&lt;char&gt; 的定义中某处说char_traits&lt;char&gt;::eof() 返回EOF
  • @Martin York EOF flag 可能未设置,但get() 仍将返回traits::eof(),如果读取失败,则将其用作一般错误代码。
【解决方案3】:

iostream 不知道它在文件末尾,直到它尝试读取文件末尾之后的第一个字符。

sample code at cplusplus.com 说要这样做:(但实际上你不应该这样做)

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

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

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

【讨论】:

  • 是否应该将“istream 读取操作...包括&lt;&lt; 运算符”改为“...包括&gt;&gt; 运算符”?
  • @Thanatos:是的。这就是我在深夜发帖所得到的。
  • 你想要!is.fail() 而不是is.good()。例如,当从文件中读取一个数字时,流必须到达文件的末尾,然后才能确定它是否读取了该数字的最后一位。在这种情况下,将成功读取一个数字,但将到达文件末尾,导致 is.good() 返回 false。这可能会导致您跳过文件中的最后一个值。相反,检查!is.fail() 在所有情况下都会做正确的事情。
  • @KenBloom - 如果您的 while(is.get(c)) 中的流是二进制的,如果字节的值为 0,这不会过早终止循环(对于二进制文件?)将 while(n=is.get() != Traits::eof()) 用于二进制文件不是更好吗?
  • @smgreenfield: 采用输出参数的 get 重载返回 istream& 用于进一步链接或错误报告。
【解决方案4】:

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

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

正确的方法是在读取操作之后检查是否到达了eof(或者,读取操作是否成功)。这是您的第二个版本所做的 - 您执行读取操作,然后使用生成的流对象引用(>> 返回)作为布尔值,从而检查失败()。

【讨论】:

  • 感谢您的解释;)
  • 它实际上检查了几个位。
猜你喜欢
  • 1970-01-01
  • 2017-04-08
  • 1970-01-01
  • 1970-01-01
  • 2017-06-25
  • 2016-06-23
  • 2011-01-30
  • 2010-09-26
  • 1970-01-01
相关资源
最近更新 更多