Search code examples
cwindowsfilebinaryfilesfseek

C binary read and write file


I am using a binary file for reading an array of integers, then each even integer x should become 2 * x and each odd integer x should become 3 * x. When I am doing this it always read the 2nd integer (which is 2). Any idea?

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

int main(void)
{    
FILE *f;

f = fopen("inputData.txt", "w+b");
int n = 5;
int i;
for (i = 1; i <= n; ++i) {
    fwrite(&i, sizeof(int), 1, f);
}
int x;
fseek(f, 0, SEEK_SET);
while (fread(&x, sizeof(int), 1, f) == 1) {
    printf("%d ", x);
    if (x % 2 == 0) {
        fseek(f, -sizeof(int), SEEK_CUR);
        x = x * 2;
        fwrite(&x, sizeof(int), 1, f);
    } else {
        fseek(f, -sizeof(int), SEEK_CUR);
        x = 3 * x;
        fwrite(&x, sizeof(int), 1, f);
    }
}

fclose(f);
}

Solution

  • Okay, I don't really understand what's going on, but it seems that you cannot trust fseek with SEEK_CUR when using with read/write files in that case (I'm running Windows, and the standard functions are notoriously different from Linux that may be the issue)

    EDIT: Andrew's answer confirms my suspicions. My solution complies to what the standards recommend.

    What I have done to workaround the problem is to manage file position myself and seek to that position instead of implicitly relying on current file position when calling fseek.

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
     FILE *f;
    
    f = fopen("inputData.txt", "w+b");
    if (!f) { perror("cannot create input"); exit(1); }
    
    int n = 5;
    int i;
    for (i = 1; i <= n; ++i) {
        fwrite(&i, sizeof(int), 1, f);
    }
    
    
    int x;
    int pos=0;
    fseek(f, 0, SEEK_SET);
    while (fread(&x, sizeof(int), 1, f) == 1) {
        if (fseek(f, pos, SEEK_SET)) {perror("cannot seek");exit(1);}
        pos += sizeof(int);
        printf("%d %lu\n", x, ftell(f));
        if (x % 2 == 0) {
            x = x * 2;
        } else {
            x = 3 * x;
        }
        if (fwrite(&x, sizeof(int), 1, f) != 1) {perror("cannot write");exit(1);}
        if (fseek(f, pos, SEEK_SET)) {perror("cannot seek");exit(1);}
    }
    
    fclose(f);
    }
    

    now the output of the program is (with current offset)

    1 0
    2 4
    3 8
    4 12
    5 16
    

    contents of the binary file is now (as expected on a little endian architecture):

    03 00 00 00 04 00 00 00 09 00 00 00 08 00 00 00 0F 00 00 00
    

    So this is a workaround but at least it works properly.