Does fseek() move the file pointer to the beginning of the file if it was opened in "a+b" mode?
Asked Answered
C

3

13

I wish to open a file using the "a+b" mode, i.e. if it does not exist it is created automatically, but if it does I don't want to overwrite it. I want to be able to read and write to the file.

The file is binary, and I want to save records of a specific struct in it. So I want to do fseek() to the record I want and then save the record using fwrite().

The code looks as follows (MyRecord is a typedef to a struct, while FILENAME is a #define to the file's name):

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

However this code just appends the record to the end of the file, even if I set pos to 0. Why isn't fseek() with SEEK_SET working in append mode?

I know I can simply open it with "r+b" and if it fails open it with "wb", but I want to know why this doesn't work and why fseek() with SEEK_SET is leaving the file pointer at the end. Any references to places where this behaviour is documented appreciated (because I couldn't find any, or I am using the wrong keywords).

Claireclairobscure answered 3/4, 2011 at 20:42 Comment(0)
O
20

That's because in a mode, writing to the FILE* always appends to the end. fseek only sets the read pointer in this mode. This is documented in the C standard, 7.19.5.3 fopen:

Opening a file with append mode ('a' as the first character in the mode argument) causes all subsequent writes to the file to be forced to the then current end-of-file, regardless of intervening calls to the fseek function.

Ogawa answered 3/4, 2011 at 20:51 Comment(0)
T
4

Use "r+b" mode and fallback to "w+b" if it fails.

The "a+b" mode, allows you to read and append; the "r+b" allows random read and write.

The documentation for fopen describes how the file behaves with the different modes.

Triennium answered 3/4, 2011 at 20:46 Comment(4)
This has a race condition that will clobber your file.Lyda
@R..: what race condition? should I say "fallback to "wb" if it fails with errno == ENOENT"Triennium
I'm assuming a multitasking OS where another process could create the file and fill it with data after the first fopen fails but before the second fopen attempt. If OP is dealing with an embedded system or any closed system where it's known that this won't happen, it may be a non-issue.Lyda
I see @R.. thanks. If that's an issue fwrite to the same position at the same time by different processes is also an issue; the OP could use the same method to manage both issues.Triennium
L
4

Plain C does not have any sane way to achieve what you want. If you're on a POSIX system or anything remotely close, you can use fd=open(FILENAME, O_CREAT|O_RDRW, 0666) and then fdopen(fd, "rb+").

Edit: Another thing you could try, with plain C:

f = fopen(FILENAME, "a+b");
if (!f) /* ... */
tmp = freopen(0, "r+b", f);
if (tmp) f = tmp;
else /* ... */
Lyda answered 3/4, 2011 at 21:11 Comment(2)
Yes I guess that would work. I need this to work on pure ANSI C though. Still +1 for you for the good alternative.Claireclairobscure
Have you tried the freopen solution? No idea if it works in practice (freopen is ill-specified and could in theory be useless) but it might be ok.Lyda

© 2022 - 2024 — McMap. All rights reserved.