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的内容是:
使用eof()进行读取时,它总是读取一个额外的字符并显示-1。 但是inf >> c给出的正确输出是'abc'? 有人可以帮我解释一下吗?
-
感谢您指出这一点。 我在TopCoder上竞争,为了节省时间,我通常导入所有名称空间。
-1是get表示您已到达文件末尾的方式。使用std::char_traits::eof()(或std::istream::traits_type::eof())进行比较-避免为-1,这是一个神奇的数字。 (尽管另一个有点冗长-您始终可以调用istream::eof)
只有在读取尝试读取文件末尾之后才设置EOF标志。如果我有一个3字节的文件,而我只读取3个字节,则EOF为false,因为我还没有尝试读取文件末尾的内容。尽管这对于通常知道文件大小的文件来说似乎令人困惑,但是直到尝试在某些设备(例如管道和网络套接字)上进行读取时,才能知道EOF。
第二个示例的工作方式是inf >> foo将始终返回inf,其副作用是尝试读取某些内容并将其存储在foo中。如果文件为"良好",则在if或while中的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.
} |
这就是我们想要的。
-
谢谢塔纳托斯。我想我现在明白了;)
-
可能不应使用>>代替get()。请记住,>>运算符在尝试读取下一个值之前总是跳过空格,而get()一次读取每个字符。
-
@Thanatos:很抱歉打扰您,但是……流怎么样?您说过,当我们尝试读取文件末尾的字符时设置了EOF,但是当我们尝试在没有字符的输入流cin上尝试peek()时会发生什么呢?标准说traits::eof()被调用,但是当我尝试以这种方式使用peek()时,它似乎在等待输入。
-
如果您尝试窥视输入的末尾,peek将返回EOF。 但是,cin通常是您输入的终端。 (这似乎是您的情况。)"等待用户输入"与EOF不同,它只是数据源(您)很慢。 因此,peek块等待您输入。 如果输入EOF(在Linux上为Ctrl + D),则返回peek,并且eof应该为true。 否则,它将返回您键入的内容。
-
我有个问题。 该文档说bool运算符定义为!fail(),因此EOFed流应解释为true吗? 它为什么会返回false?
-
问题解决了。 在上面给出的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).
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;
} |
更好的习惯用法是将读取移入循环条件,如下所示:
(您可以使用所有返回*this的istream读取操作(包括>>运算符)执行此操作)
1 2 3
| char c;
while(is.get(c))
cout << c; |
-
" istream读取操作...(包括<<运算符)"应该读为" ...包括>>运算符"吗?
-
@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&,以进行进一步的链接或错误报告。
仅在读取操作尝试读取文件末尾之后才设置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标志。"正是引起EOF混淆的陈述。仅在读取操作尝试读取文件末尾之后才设置EOF标志。最后一点很关键:如果我从3字节长的文件中读取3字节,则EOF为假,直到尝试再次读取。
-
iostream不返回EOF常量。 int istream::get()的返回值在文件末尾似乎是未定义的-您必须检查istream::eof()
-
@肯·布鲁姆:你确定吗?我的标准说:"返回:c(如果可用),否则为traits::eof()。" 27.6.1.3(草稿2,96年12月2日)
-
您是对的,但这与C stdio使用的EOF常数相去甚远。我错了。在char_traits的定义中某处说char_traits::eof()返回EOF
-
@Ken Bloom刚刚找到21.1.3.1 struct char_traits部分:"成员eof()返回EOF。"
-
@Justin,我也刚在您找到它的同时找到它。
-
@肯·布鲁姆:我的观点并没有那么多,它可能会返回EOF,也可能不会返回EOF,尽管对此,我很好地知道,我不知道EOF和char_traits::eof()是否被保证相等,但是返回值被很好地定义为某个地方的常数
-
@Justin Spahr-Summers:感谢您提供另一个出色的解决方案;)
-
在这种情况下,您仍不应测试EOF。如果流中出现其他错误并设置了坏位,该怎么办。然后,流将永远不会返回EOF。
-
@Martin York可能未设置EOF标志,但get()仍将返回traits::eof(),如果读取失败,它将用作一般错误代码。
-
@肯·布鲁姆:在哪里可以找到C ++引用的标准?谢谢!
-
@Chan:您必须购买标准,但是我们大多数人都在看N1905工作草案。
eof()检查流状态下的eofbit。
在每次读取操作中,如果位置位于流的末尾并且必须读取更多数据,则将eofbit设置为true。因此,在获得eofbit = 1之前,您将获得一个额外的字符。
正确的方法是在读取操作之后检查是否达到了eof(或读取操作是否成功)。这是您的第二个版本所执行的操作-您执行读取操作,然后将所得的流对象引用(>>返回)用作布尔值,这将导致对fail()的检查。