feof() в Linux возвращает true на одну строку позже, чем конечная строка

Я новичок в C. Когда я использую этот цикл while для печати содержимого файла. Последняя строка будет напечатана дважды в Linux. Он не должен попадать в цикл while при достижении конца файла. На винде проблем нет.

#include <stdio.h>
#include <unistd.h>

int main()
{

    char string[400];
    FILE *file_para;

    // Open the file
    if ((file_para = fopen("Test.txt", "r")) == NULL)
    {
        printf("cannot open file\n");
        getchar();
        return 0;
    }

    while (!feof(file_para))
    {
        fgets(string, 400, file_para);
        printf("**** %s", string);
    }

    fclose(file_para);
    getchar();
    return 0;
}

person Dan    schedule 17.09.2013    source источник
comment
Используйте fgets(...) в качестве условия цикла. Он вернет NULL, если не сможет прочитать больше символов.   -  person user123    schedule 17.09.2013
comment
Проблема с проверкой feof (или, по крайней мере, о чем следует знать) заключается в том, что она задокументирована: этот индикатор обычно устанавливается предыдущей операцией над потоком, который пытался прочитать в конце файла или за ним. Таким образом, вы можете оказаться прямо в конце файла, и он все равно не будет указывать EOF, пока вы не выполните другое чтение. Таким образом, последнее fgets, которое вы сделали, может оказаться за концом файла. Как указывает @MohammadAliBaydoun, вы должны использовать свой вызов fgets как чтение и проверку EOF для цикла.   -  person lurker    schedule 17.09.2013
comment
Большое Вам спасибо. Оно работает.   -  person Dan    schedule 17.09.2013


Ответы (2)


Это неправильный способ использования feof(). Используйте feof(), чтобы определить, что пошло не так после отказа одной из основных функций ввода/вывода. Он не предсказывает, собираетесь ли вы достичь EOF; он сообщает вам, когда какая-либо функция ввода-вывода уже сообщила об окончании операции. C — это не Паскаль; в Паскале вы можете (должны?) проверять EOF перед вызовом функций ввода/вывода.

while (fgets(string, sizeof(string), file_para) != 0)
{
    ...do printing, etc...
}
// If you need to, use `feof()` and `ferror()` to sort out what went wrong.

Если вы действительно настаиваете на использовании feof(), вам также необходимо проверить операцию ввода-вывода:

while (!feof(file_para))
{
    if (fgets(string, sizeof(string), file_para) == 0)
        break;
    ...do printing, etc...
}

Обратите внимание, что вы можете ошибаться, потому что ferror(file_para) оценивается как true, даже если feof(file_para) нет... так что, возможно, вам нужен while (!feof(file_para) && !ferror(file_para)), но на самом деле это просто еще одно доказательство того, что цикл while должен быть обусловлен функцией ввода-вывода, а не feof().

person Jonathan Leffler    schedule 17.09.2013

Это распространенный анти-шаблон:

while (!feof(file_para))
{
    fgets(string, 400, file_para);

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

Правильный узор такой:

while (fgets(string, 400, file_para))
{
person caf    schedule 17.09.2013