Вдохновленный моим предыдущим вопросом
Общей ошибкой для новых программистов на 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()
в качестве нашего условия извлечения?