Прочитать файл дважды на С++ из-за eof?

Я читаю числа из файла, применяю 3 функции и распечатываю в другой файл:

int main(int argc, char** argv) {
    std::ifstream fin;
    fin.open("input.txt");

    std::ofstream fout;
    fout.open("output.txt", std::ios::app);

    char arr[50];
    int a,b;
    int N;//number to factor

    while (!fin.eof()){
        //Print backward
        fin >> arr;
        PrintBackward( arr );
        fout << endl;

        //Greatest common divisor
        ((fin >> a) >> b);
        fout << gcd( a, b );
        fout << endl;

        //Find prime factor
        fin >> N;
        PrimeFactor(N);
        fout << endl;     
    }

    fin.close();
    fout.close();

    return 0;
}

После запуска результат дублируется:

olleh
3
2 3 7 
olleh
3
2 3 7

Я читал аналогичную статью, но она о чтении в 1 переменную, поэтому это кажется неосуществимым.

Если я установлю break в конце цикла while, все в порядке. Есть ли способ не использовать break?


person Duc Tran    schedule 16.04.2011    source источник


Ответы (2)


while (!whatever.eof()) по существу всегда ошибочен и никогда не определит правильно конец файла. В вашем случае проще всего объединить чтения вместе, а затем выполнить всю обработку, например так:

while (fin >> arr >> a >> b >> N) {
    PrintBackwards(arr);
    fout << "\n";

    fout << gcd(a, b) << "\n";

    fout << PrimeFactor(N) << "\n";
}

Важнейшей частью является проверка результата чтения, а не проверка и чтение отдельно друг от друга.

Еще пара советов: я бы использовал std::string вместо массива. Я бы также отделил реверсирование строки от ее печати, поэтому вы можете получить что-то вроде:

fout << reverse(arr)   << "\n" 
     << gcd(a, b)      << "\n" 
     << PrimeFactor(N) << "\n";

Подчеркивание общности между операциями, как правило, является хорошей идеей.

Редактировать: просто для удовольствия я укажу еще один способ, которым вы могли бы что-то делать, если хотите. Поскольку вы в основном читаете и обрабатываете четыре элемента как группу, вы можете сделать эту группировку немного более явной:

struct item { 
     std::string arr;
     int a, b, N;

     friend std::istream &operator>>(std::istream &is, item &i) { 
         return is >> arr >> a >> b >> N;
     }
};

struct process {
    std::string operator()(item const &i) { 
        std::ostringstream buffer;

        buffer << reverse(arr) << "\n" << gcd(a, b) << "\n" << PrimeFactor(N);
        return buffer.str();
    }
}

При этом вы можете позволить стандартной библиотеке справиться со всеми деталями чтения и записи, проверкой конца файла и т. д.:

std::transform(std::istream_iterator<item>(fin), 
               std::istream_iterator<item>(),
               std::ostream_iterator<std::string>(std::cout, "\n"),
               process());
person Jerry Coffin    schedule 16.04.2011
comment
while (!whatever.eof()) is essentially always wrong. ... Я не мог понять, почему? Просто любопытно узнать. Спасибо. - person iammilind; 16.04.2011
comment
Потому что вам нужно попытаться прочитать данные, проверить, не произошел ли сбой, а затем обработать их, если чтение завершилось успешно, или выйти из цикла, если оно не удалось. while (!whatever.eof()) выполняет проверку, затем читает, пытается обработать данные (даже если чтение не удалось) и только после неправильной обработки данных после неудачного чтения обнаруживает, что чтение не удалось, и выходит из цикла. - person Jerry Coffin; 16.04.2011

Я предполагаю, что вы слишком рано проверяете eof - он устанавливается только тогда, когда вы пытаетесь прочитать, а чтение завершается ошибкой, потому что вы находитесь в конце файла. Попробуйте добавить это после fin >> arr:

if (fin.eof()) break;

На самом деле вы должны проверять наличие ошибок после каждой операции ввода-вывода - не делать этого - неаккуратное кодирование и не будет надежным.

person Raph Levien    schedule 16.04.2011
comment
Не угадай. Знать ответ. Как мы можем доверять вашему объяснению, если вы гадаете. Если вы не знаете, напишите код, чтобы проверить свою догадку. Вы можете объединить чтения и проверки в один оператор (как это делают большинство языков), поместив чтение в проверку условия while. while (fin >> arr) . Лично я бы пошел дальше. - person Martin York; 16.04.2011