Search code examples
ctruncatemsys2

ftruncate is not working properly with msys2


I'm trying to modify the size of a file. I'm working with msys2. My code looks like this:

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

void dummy_file(){
    FILE *f = fopen("file.txt", "w");
    if (f == NULL)
    {
        printf("Error opening file!\n");
        exit(1);
    }

    /* print some text */
    const char *text = "Write this to the file";
    fprintf(f, "Some text: %s\n", text);

    /* print integers and floats */
    int i = 1;
    float py = 3.1415927;
    fprintf(f, "Integer: %d, float: %f\n", i, py);

    /* printing single chatacters */
    char c = 'A';
    fprintf(f, "A character: %c\n", c);
    fclose(f);
}

void print_size(){
    FILE *f = fopen("file.txt", "r");
    long sz = 0;
    if (f == NULL)
    {
        printf("Error opening file!\n");
        exit(1);
    }
    fseek(f, 0L, SEEK_END);
    sz = ftell(f);
    printf("%ld\n",sz);
    fclose(f);
}

void truncate_file(){
    FILE *f = fopen("file.txt", "w");
    if (f == NULL)
    {
        printf("Error opening file!\n");
        exit(1);
    }
    ftruncate(fileno(f), 40);
    fclose(f);
}

int main()
{
    printf("Begin\n");
    dummy_file();
    print_size();
    // truncate_file();
    print_size();
    printf("End\n");
    return 0;
}

The output looks like this:

Begin 80 40 End

The file has changed to 40 bytes but the content is plenty of null values.

I'm doing something wrong? Is there an alternative to truncate the file while maintaining its content?


Solution

  • When you open a file with fopen on w mode you truncate the file to zero length. After that, when you run ftruncate it will fill the file with \0 to get to the size you specified.

    From fopen man page,

    w Truncate file to zero length or create text file for writing. The stream is positioned at the beginning of the file.

    You can instead open it with r+ mode, which doesn't truncate the file if it exists and allows for writing as well.

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    void dummy_file(){
        FILE *f = fopen("file.txt", "w");
        if (f == NULL)
        {
            printf("Error opening file!\n");
            exit(1);
        }
    
        /* print some text */
        const char *text = "Write this to the file";
        fprintf(f, "Some text: %s\n", text);
    
        /* print integers and floats */
        int i = 1;
        float py = 3.1415927;
        fprintf(f, "Integer: %d, float: %f\n", i, py);
    
        /* printing single chatacters */
        char c = 'A';
        fprintf(f, "A character: %c\n", c);
        fclose(f);
    }
    
    void print_size(){
        FILE *f = fopen("file.txt", "r");
        long sz = 0;
        if (f == NULL)
        {
            printf("Error opening file!\n");
            exit(1);
        }
        fseek(f, 0L, SEEK_END);
        sz = ftell(f);
        printf("%ld\n",sz);
        fclose(f);
    }
    
    void truncate_file(){
        FILE *f = fopen("file.txt", "r+");
        if (f == NULL)
        {
            printf("Error opening file!\n");
            exit(1);
        }
        ftruncate(fileno(f), 40);
        fclose(f);
    }
    
    int main()
    {
        printf("Begin\n");
        dummy_file();
        print_size();
        truncate_file();
        print_size();
        printf("End\n");
        return 0;
    }
    

    Just a side note to consider when expanding your code: Mixing file descriptor and STD I/O Streams can result in undefined behavior if some steps aren't followed (see this answer which points to this link for more information).