Перемещает ли fseek() указатель файла в начало файла, если он был открыт в режиме a+b?

Я хочу открыть файл в режиме "a+b", т.е. если он не существует, он создается автоматически, но если он существует, я не хочу его перезаписывать. Я хочу иметь возможность читать и писать в файл.

Файл бинарный, и я хочу сохранить в нем записи определенного struct. Итак, я хочу сделать fseek() для записи, которую хочу, а затем сохранить запись, используя fwrite().

Код выглядит следующим образом (MyRecord — это typedef для struct, а FILENAME — это #define для имени файла):

int saveRecord(MyRecord *pRecord, int pos)
{
    FILE* file = fopen(FILENAME, "a+b");
    if (file == NULL)
    {
        printf("Unable to open file %s\n", FILENAME);
        return 0;
    }

    fseek(file, pos * sizeof(MyRecord), SEEK_SET);
    fwrite(pRecord, sizeof(MyRecord), 1, file);
    fclose(file);
    return 1;
}

Однако этот код просто добавляет запись в конец файла, даже если я установил pos в 0. Почему fseek() с SEEK_SET не работает в режиме добавления?

Я знаю, что могу просто открыть его с помощью «r+b», а если это не удастся, открыть его с помощью «wb», но я хочу знать, почему это не работает и почему fseek() с SEEK_SET оставляет указатель файла в конце. Приветствуются любые ссылки на места, где задокументировано такое поведение (потому что я не смог их найти или использую неправильные ключевые слова).


person jbx    schedule 03.04.2011    source источник


Ответы (3)


Это потому, что в режиме a запись в FILE* всегда добавляется в конец. fseek устанавливает только указатель чтения в этом режиме. Это задокументировано в стандарте C, 7.19.5.3 fopen:

Открытие файла в режиме добавления ('a' в качестве первого символа в аргументе режима) приводит к тому, что все последующие записи в файл принудительно выполняются в текущий конец файла, независимо от промежуточных вызовов функции fseek.

person Fred Foo    schedule 03.04.2011

У простого C нет разумного способа добиться того, чего вы хотите. Если вы используете систему POSIX или что-то близкое к ней, вы можете использовать fd=open(FILENAME, O_CREAT|O_RDRW, 0666), а затем fdopen(fd, "rb+").

Редактировать: Еще одна вещь, которую вы можете попробовать, используя простой C:

f = fopen(FILENAME, "a+b");
if (!f) /* ... */
tmp = freopen(0, "r+b", f);
if (tmp) f = tmp;
else /* ... */
person R.. GitHub STOP HELPING ICE    schedule 03.04.2011
comment
Да, я думаю, это сработает. Мне нужно, чтобы это работало на чистом ANSI C. Еще +1 вам за хорошую альтернативу. - person jbx; 04.04.2011
comment
Вы пробовали решение freopen? Не знаю, работает ли это на практике (freopen плохо определено и теоретически может быть бесполезным), но может быть все в порядке. - person R.. GitHub STOP HELPING ICE; 04.04.2011

Используйте режим «r+b» и вернитесь к «w+b», если он не работает.

Режим «a+b» позволяет читать и добавлять; «r+b» разрешает произвольное чтение и запись.

Документация для fopen описывает поведение файла в разных режимах.

person pmg    schedule 03.04.2011
comment
Это состояние гонки, которое уничтожит ваш файл. - person R.. GitHub STOP HELPING ICE; 04.04.2011
comment
@R..: какое состояние гонки? должен ли я использовать резервный вариант wb, если он не работает с errno == ENOENT - person pmg; 04.04.2011
comment
Я предполагаю многозадачную ОС, в которой другой процесс может создать файл и заполнить его данными после первой попытки fopen, но до второй попытки fopen. Если OP имеет дело со встроенной системой или любой закрытой системой, где известно, что этого не произойдет, это может не быть проблемой. - person R.. GitHub STOP HELPING ICE; 04.04.2011
comment
Я вижу @R.. спасибо. Если это проблема fwrite в одну и ту же позицию в одно и то же время разными процессами, это тоже проблема; ОП может использовать один и тот же метод для решения обеих проблем. - person pmg; 04.04.2011