受到我之前的问题启发
新手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),然后返回。
我们可以通过一个简单的例子来证明这一点,该例子使用了一个 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()
作为我们的提取条件的真正原因是什么?
if(!(stream>>var)) { doErrorHandling(); }
- CoffeDeveloperstream
即可。在那里进行五次单独的检查只会让事情变得混乱。 - Lightness Races in Orbit