受我上一个问题的启发
新 C++ 程序员的一个常见错误是从文件中读取以下内容:
std::ifstream file("foo.txt");
std::string line;
while (!file.eof()) {
file >> line;
// Do something with line
}
他们经常会报告文件的最后一行被读取了两次。这个问题的常见解释(我之前给出的)是这样的:
如果您尝试提取文件结尾,则提取只会在流上设置 EOF 位,而不是如果您的提取只是在文件结尾处停止。
file.eof()
只会告诉您前一次读取是否到达文件末尾,而不是下一次读取是否会。在提取了最后一行之后,EOF 位仍未设置,并且迭代再发生一次。然而,在最后一次迭代中,提取失败并且line
仍然具有与之前相同的内容,即最后一行被复制。
但是,这个解释的第一句话是错误的,所以代码在做什么的解释也是错误的。
格式化输入函数(即operator>>(std::string&)
)的定义将提取定义为使用rdbuf()->sbumpc()
或rdbuf()->sgetc()
获取输入字符。它指出,如果这些函数中的任何一个返回traits::eof()
,则设置 EOF 位:
如果
rdbuf()->sbumpc()
或rdbuf()->sgetc()
返回traits::eof()
,则输入函数,除非另有明确说明,否则会在返回之前完成其操作并执行setstate(eofbit)
,这可能会抛出ios_base::failure
(27.5.5.4)。
我们可以通过使用 a 而不是文件的简单示例看到这一点std::stringstream
(它们都是输入流并且在提取时表现相同):
int main(int argc, const char* argv[])
{
std::stringstream ss("hello");
std::string result;
ss >> result;
std::cout << ss.eof() << std::endl; // Outputs 1
return 0;
}
这里很清楚,单次提取hello
是从字符串中获取的,并将 EOF 位设置为 1。
那么解释有什么问题呢?!file.eof()
导致最后一行重复的文件有什么不同?我们不应该将!file.eof()
其用作提取条件的真正原因是什么?