Чтение из текстового файла до тех пор, пока EOF не повторит последнюю строку
Вопрос
На этот вопрос уже есть ответ здесь:
Следующее С++ код использует еслипоток объект для чтения целых чисел из текстового файла (который имеет одно число в строке), пока не достигнет ЭОФ.Почему он читает целое число в последней строке дважды?Как это исправить?
Код:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream iFile("input.txt"); // input.txt has integers, one per line
while (!iFile.eof())
{
int x;
iFile >> x;
cerr << x << endl;
}
return 0;
}
input.txt:
10
20
30
Выход:
10
20
30
30
Примечание:Я пропустил весь код проверки ошибок, чтобы фрагмент кода был небольшим.Вышеупомянутое поведение наблюдается в Windows (Visual C++), cygwin (gcc) и Linux (gcc).
Решение
Просто внимательно следите за цепочкой событий.
- Возьмите 10
- Возьмите 20
- Возьмите 30
- Получить EOF
Посмотрите на предпоследнюю итерацию.Вы взяли 30, а затем продолжили проверку на EOF.Вы не достигли EOF, потому что метка EOF еще не прочитана (говоря в бинарном формате, ее концептуальное расположение находится сразу после 30-й строки).Поэтому вы переходите к следующей итерации.x по-прежнему равен 30 по сравнению с предыдущей итерацией.Теперь вы читаете из потока и получаете EOF.x остается 30, а ios::eofbit повышается.Вы выводите данные в stderr x (30, как и в предыдущей итерации).Затем вы проверяете наличие EOF в условии цикла, и на этот раз вы выходите из цикла.
Попробуй это:
while (true) {
int x;
iFile >> x;
if( iFile.eof() ) break;
cerr << x << endl;
}
Кстати, в вашем коде есть еще одна ошибка.Вы когда-нибудь пытались запустить его с пустым файлом?Поведение, которое вы получаете, происходит по той же причине.
Другие советы
Мне нравится этот пример, в котором пока не учтена проверка, которую можно было бы добавить внутри блока while:
ifstream iFile("input.txt"); // input.txt has integers, one per line
int x;
while (iFile >> x)
{
cerr << x << endl;
}
Не знаю, насколько это безопасно...
Есть альтернативный подход к этому:
#include <iterator>
#include <algorithm>
// ...
copy(istream_iterator<int>(iFile), istream_iterator<int>(),
ostream_iterator<int>(cerr, "\n"));
Шаблону EOF требуется простое чтение для «запуска» процесса проверки EOF.Предположим, что для пустого файла изначально не будет установлен EOF до первого чтения.В этом случае простое чтение перехватит EOF и полностью пропустит цикл.
Здесь вам нужно помнить, что вы не получите EOF до первой попытки прочитать доступные данные файла.Чтение точного объема данных не будет сигнализировать о EOF.
Я должен отметить, что если бы файл был пуст, ваш код был бы напечатан, поскольку EOF не позволит установить значение x при входе в цикл.
- 0
Поэтому добавьте простое чтение и переместите чтение цикла в конец:
int x;
iFile >> x; // prime read here
while (!iFile.eof()) {
cerr << x << endl;
iFile >> x;
}
Без особых изменений исходного кода это могло бы выглядеть так:
while (!iFile.eof())
{
int x;
iFile >> x;
if (!iFile.eof()) break;
cerr << x << endl;
}
но в целом я предпочитаю два других решения, приведенных выше.
int x;
ifile >> x
while (!iFile.eof())
{
cerr << x << endl;
iFile >> x;
}
В конце последней строки находится символ новой строки, который не читается оператором >> и не является концом файла.Пожалуйста, поэкспериментируйте и удалите новую строку (последний символ в файле) - дублирования не будет.Чтобы иметь гибкий код и избежать нежелательных эффектов, просто примените любое решение, предложенное другими пользователями.