【问题标题】:Why is ::feof() different from ::_eof(::fileno())?为什么 ::feof() 与 ::_eof(::fileno()) 不同?
【发布时间】:2012-02-05 16:44:13
【问题描述】:

免责声明:不要使用::feof() 作为循环条件。例如,请参阅答案:file reading: feof() for binary files

但是,我有“真实”代码演示了一个不使用 ::feof() 作为我的循环条件的问题,但从逻辑上讲,这是演示该问题的最简单方法。

考虑以下情况:我们一次迭代一个字符流:

FILE* my_file;
// ...open "my_file" for reading...

int c;
while(0 == ::feof(my_file))
{ // We are not at EOF
  c = ::getc(my_file);
  // ...process "c"
}

上面的代码按预期工作:文件一次处理一个字符,在EOF时,我们退出。

但是,以下有意外行为

FILE* my_file;
// ...open "my_file" for reading...

int c;
while(0 == ::_eof(::fileno(my_file)))
{ // We are not at EOF
  c = ::getc(my_file);
  // ...process "c"
}

我原以为他们会执行相同的操作。 ::fileno() 每次都正确返回(整数)文件描述符。但是,测试::_eof(::fileno(my_file))只运行一次,然后在第二次尝试时返回1(表示EOF)。

我不明白。

我想::feof() 是“缓冲的”(因此它可以正常工作)是可以想象的,而::_eof() 是“未缓冲的”并且认为整个文件已经是“读入”的(因为整个文件会已适合从磁盘读取的第一个块)。但是,考虑到这些功能的目的,这不可能是真的。所以,我真的很茫然。

发生了什么事?

(文件以“文本”形式打开,是大约十几行的 ASCII 文本文件,MSVS2008,Win7/64。)

【问题讨论】:

  • 你应该检查 getc() 的返回值来检查 EOF。 if(c!=EOF) cplusplus.com/reference/clibrary/cstdio/getc
  • 您的代码仍然不正确。您必须检查 EOF after 读取但 before 处理。在遇到文件结尾之前,您不会看到它。
  • 同意上述两个 cmets (@Rajendran && @Kerrek)。但是,这些问题在我的用例中不存在:文件不是空的,并且有多行; feof() 工作正常; _eof(fileno()) 在读取第一个字符后失败(返回 1)。因此,那些关于我的“不正确条件”的合法投诉不适用于本案。 (这就是我以免责声明开头的原因。)
  • 事实上它确实适用于您的情况。 Rajendran 和 Kerrek 所说的是与您链接的问题不同的问题,并且不仅适用于空文件。 feof 在您读取文件的最后一个字节后不返回 1,在您尝试读取文件末尾的 PAST 后它返回 1。因此,您将 EOF 作为循环中的字符处理。
  • @Gerald,我没有看到/理解它:在一个包含多行的文件上恰好有一个getc() 之后,_eof() 返回1。它在第一次读取之前返回0,并且第一次读取(正确)返回一个字符(并且不在EOF)。我仍在尝试理解您在下面的答案,以及 _eof() 的目的是什么,如果它总是返回 1,即使您没有阅读任何内容(在第一个字符之后)。

标签: c++ file-io stream filestream


【解决方案1】:

我想 ::feof() 是“缓冲的”是可以想象的(所以它可以工作 正确)而 ::_eof() 是“未缓冲”并认为整个文件 已经“读入”(因为整个文件都适合 从磁盘读取的第一个块)。然而,这不可能是真的 鉴于这些功能的目的。所以,我真的很茫然。

我不知道为什么您会认为“考虑到这些功能的目的,这不可能是真的”。这2个函数是为了对不同方式打开和操作的文件进行操作,所以不兼容。

事实上,这正是正在发生的事情。试试这个:

FILE* my_file;
// ...open "my_file" for reading...

int c;
while(0 == ::_eof(::fileno(my_file)))
{ // We are not at EOF
  c = ::getc(my_file);

  long offset1 = ftell(my_file);
  long offset2 = _tell(fileno(my_file));

  if (offset1 != offset2)
  {
     //here you will see that the file pointers are different
     //which means that _eof and feof will fire true under different conditions
  }
  // ...process "c"
}

我会根据您的评论尝试详细说明。

当您调用 fopen 时,您将返回一个指向文件 的指针。底层流对象保留它自己的文件指针,该指针与与底层文件描述符关联的实际文件指针分开。

当您调用 _eof 时,您是在询问您是否已到达实际文件的末尾。当您调用 feof 时,您是在询问您是否已到达文件 stream 的末尾。由于文件流通常是缓冲的,所以在流结束之前到达文件的末尾。

我仍在尝试理解您在下面的回答,以及目的是什么 用于 _eof() 如果即使您没有阅读它也总是返回 1 任何东西(在第一个字符之后)。

要回答这个问题,_eof 的目的是确定您在使用 _open 和 _read 直接处理文件描述符时是否已到达文件末尾,而不是当您使用 fopen 和 fread 或 getc 处理文件流时.

【讨论】:

  • 我花了一些时间才得出结论/理解@Gerald 的回答是正确的。谢谢@Gerald!
猜你喜欢
  • 2016-10-12
  • 2011-07-22
相关资源
最近更新 更多