Search code examples
cfiletext

Why setting file position and writing in it, fill with null char in C?


I was trying to write in a arbitrary position in a text file in C.


#include <stdio.h>

void write_in_file(FILE *file, char *str, fpos_t *pos){
    fsetpos(file, pos);
    for(int i=0; str[i] != '\0'; ++i){
       fputc(str[i],file);
            }
}

int main(){

    char *str = "Hola123 Mundo\n";
    FILE *file = fopen("database/file_1.txt", "w");

    fpos_t pos = 1;
    write_in_file(file, str, &pos);

    fclose(file);
    return 0;
}

and that text file has previous information in it. I first write "Hola123 Mundo\n" with pos = 0, and then rewrite with pos = 1. I was expected the output file to be

HHola123 Mundo

but a got

'\0'Hola123 Mundo

I know the first byte is a null character because in binary is 00 in the hex editor.

My intuition was that when open with "w" mode, rewrite the entire file, so skipping the first position result in skipping a 00 byte. Then I tried "w+" mode, but had the same error.


Solution

  • For starters: fpos_t is an opaque type. The only correct way to use fpos_t is to use a value previously obtained via fgetpos(). If you want to set a file to a fixed offset, use fseek() instead:

    void write_in_file(FILE *file, char *str, long pos) {
        fseek(file, pos, SEEK_SET);
        for (int i = 0; str[i] != '\0'; ++i) {
            fputc(str[i], file);
        }
    }
    

    Furthermore you could use fputs() instead of a loop:

    void write_in_file(FILE *file, char *str, long pos) {
        fseek(file, pos, SEEK_SET);
        fputs(str, file);
    }
    

    To answer the original question, you never wrote your string to your file at an offset of 0 after opening the file, and opening the file with the "w" mode truncates the file to 0 length, deleting any data already there. When you write with an offset of 1 to an empty file, the first byte is non-deterministic and in your case was set to a 0.

    If you did:

    write_in_file(file, str, 0);
    write_in_file(file, str, 1);
    

    Or (Provided your file already started with H):

    FILE *file = fopen("database/file_1.txt", "r+");
    

    Instead of just:

    // fpos_t pos = 1;
    // write_in_file(file, str, &pos);
    write_in_file(file, str, 1);
    

    And:

    FILE *file = fopen("database/file_1.txt", "w");
    

    Respectively, then your file would contain:

    HHola123 Mundo