关于c ++:ifstream的eof()如何工作?

How does ifstream's eof() work?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <fstream>

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

我真的对eof()函数感到困惑。 假设我的ex.txt的内容是:

1
abc

使用eof()进行读取时,它总是读取一个额外的字符并显示-1。 但是inf >> c给出的正确输出是'abc'? 有人可以帮我解释一下吗?


-1是get表示您已到达文件末尾的方式。使用std::char_traits::eof()(或std::istream::traits_type::eof())进行比较-避免为-1,这是一个神奇的数字。 (尽管另一个有点冗长-您始终可以调用istream::eof)

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

第二个示例的工作方式是inf >> foo将始终返回inf,其副作用是尝试读取某些内容并将其存储在foo中。如果文件为"良好",则在ifwhile中的inf将求值为true:无错误,无EOF。因此,当读取失败时,inf会评估为false,并且循环会正常终止。但是,请考虑以下常见错误:

1
2
3
4
5
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.
}

但是,这:

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

这就是我们想要的。


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

cplusplus.com上的示例代码说要这样做:(但是您实际上不应该这样)

1
2
3
4
5
6
  while (is.good())     // loop while extraction from file is possible
  {
    c = is.get();       // get character from file
    if (is.good())
      cout << c;
  }

更好的习惯用法是将读取移入循环条件,如下所示:
(您可以使用所有返回*thisistream读取操作(包括>>运算符)执行此操作)

1
2
3
  char c;
  while(is.get(c))
    cout << c;


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

1
2
3
4
5
int ch;
while ((ch = inf.get()) != EOF) {
    std::cout << static_cast<char>(ch) <<"
"
;
}


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

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

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