Чтение из текстового файла до тех пор, пока EOF не повторит последнюю строку

Следующий код С++ использует объект ifstream для чтения целых чисел из текстового файла (который имеет по одному числу в строке), пока не достигнет значения 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 С++), cygwin (gcc) и Linux (gcc).

Ответ 1

Просто следуйте цепочке событий.

  • Захватить 10
  • Grab 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;
}

Кстати, в вашем коде есть еще одна ошибка. Вы когда-нибудь пытались запустить его в пустом файле? Поведение, которое вы получаете по той же причине.

Ответ 2

Мне нравится этот пример, который на данный момент не учитывает проверку, которую вы можете добавить внутри блока while:

ifstream iFile("input.txt");        // input.txt has integers, one per line
int x;

while (iFile >> x) 
{
    cerr << x << endl;
}

Не уверен, насколько это безопасно...

Ответ 3

Есть альтернативный подход к этому:

#include <iterator>
#include <algorithm>

// ...

    copy(istream_iterator<int>(iFile), istream_iterator<int>(),
         ostream_iterator<int>(cerr, "\n"));

Ответ 4

Без больших модификаций исходного кода это может стать:

while (!iFile.eof())
{  
    int x;
    iFile >> x;
    if (!iFile.eof()) break;
    cerr << x << endl;
}

но я предпочитаю два других решения выше в целом.

Ответ 5

Шаблон EOF требует простого чтения для "начальной загрузки" процесса проверки EOF. Рассмотрим, что пустой файл сначала не будет иметь EOF до первого чтения. Первое чтение будет захватывать EOF в этом случае и полностью пропустить цикл полностью.

Что вам нужно запомнить здесь, так это то, что вы не получаете EOF до первой попытки прочесть имеющиеся данные файла. Чтение точного объема данных не будет означать EOF.

Я должен указать, был ли файл пустым, ваш заданный код был бы напечатан, так как EOF предотвратит установку значения в x при входе в цикл.

  • 0

Итак, добавьте простейшее чтение и переместите цикл в конец:

int x;

iFile >> x; // prime read here
while (!iFile.eof()) {
    cerr << x << endl;
    iFile >> x;
}

Ответ 6

int x;
ifile >> x

while (!iFile.eof())
{  
    cerr << x << endl;        
    iFile >> x;      
}

Ответ 7

В конце последней строки у вас есть новый символ строки, который не читается оператором → , и это не конец файла. Проведите эксперимент и удалите новую строку (текущий символ в файле) - вы не получите дублирование. Чтобы иметь гибкий код и избегать нежелательных эффектов, просто примените любое решение, предоставленное другими пользователями.