cfilesystems

Random access of a file with fseek in C


I am trying to write some data in the middle of an existing file, however, it isn't clear what mode to open with. If I open with "w" it'll truncate the file, so "a" seems the obvious choice. What I'd want to do is open with "a" and then seek to the necessary position. However, when I do that it always writes to the end:

FILE* t = fopen("ttt", "w");
fprintf(t, "1234567890");
fclose(t);
t = fopen("ttt", "a");
int result = fseek(t, 2, SEEK_SET);
fprintf(stderr, "%d", result);
fprintf(t, "xxx");
fclose(t);

I'd expect the test file ttt to contain "12xxx67890" but it contains "1234567890xxx". (Result of fseek is 0.)

Any suggestions on how to accomplish this?


Solution

  • I'd expect the test file ttt to contain "12xxx67890" but it contains "1234567890xxx". (Result of fseek is 0.)

    fseek() is not the problem. You can still reposition the pointer with it, but each write in append mode will set it to point to the end of the file.

    cppreference - fopen, fopen_s:

    On the append file access modes, data is written to the end of the file regardless of the current position of the file position indicator.

    See this question on fseek() in append mode. This behavior can be useful when using "a+" to read from the file as well as writing.


    To work around this you can use read extended mode "r+". This lets you read and write to the file without you having to close and reopen it.

    cppreference - fopen, fopen_s:

    In update mode ('+'), both input and output may be performed, but output cannot be followed by input without an intervening call to fflush, fseek, fsetpos or rewind, and input cannot be followed by output without an intervening call to fseek, fsetpos or rewind, unless the input operation encountered end of file. In update mode, implementations are permitted to use binary mode even when text mode is specified.

    A working solution might look like this:

    #include <stdio.h>
    
    int main() {
        FILE* t = fopen("ttt", "w");
    
        fprintf(t, "1234567890");
        fclose(t);
    
        t = fopen("ttt", "r+");
        int result = fseek(t, 2, SEEK_SET);
    
        fprintf(stderr, "%d", result);
        fprintf(t, "xxx");
    
        fclose(t);
        return 0;
    }
    

    You could now be tempted to just use "r+" once at the start of your program since it can be used to perform both write actions you use but note that it will not create a new file if it does not exists yet.