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

StackOverflow https://stackoverflow.com/questions/21647

  •  09-06-2019
  •  | 
  •  

Вопрос

Следующее С++ код использует еслипоток объект для чтения целых чисел из текстового файла (который имеет одно число в строке), пока не достигнет ЭОФ.Почему он читает целое число в последней строке дважды?Как это исправить?

Код:

#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;      
}

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top