Albus Dumbledore
Albus Dumbledore

Reputation: 12616

Reading a binary file in C: ftell returns results that sometimes are off by one

I'm trying to read a binary file that is in the following format:

number of images [4-byte int]

width [4-byte int]
height [4-byte int]
grayscale data [width * height bytes]
(more elements of the same type)

That's the first function being called:

int process_file(const char *filename) {
    FILE *input_file = fopen(filename, "r");

    //Get number of images
    unsigned int number_of_images = read_int_from_file(input_file);

    //Allocate memory for all images
    struct image *images = malloc(sizeof(struct image) * number_of_images);

    read_images(images, number_of_images, input_file)
}

Here's the image structure for anyone wondering:

struct image {
    unsigned int width;
    unsigned int height;
    unsigned char *data;
};

And that's what read_int_from_file does:

static unsigned int read_int_from_file(FILE *file) {
    unsigned char chars[4];
    if (fread(&chars, sizeof(char), 4, file) != 4) {
        fprintf(stderr, "Couldn't read enough bytes!\n");
        return 0;
    }
    //Calculations follow that return right numbers
    return out;
}

That's the rest:

static int read_images(struct image *images, unsigned int number_of_images, FILE * file) {
    struct image *current_image = images;

    int i;
    for (i = 0; i < number_of_images; i++) {
        read_image(current_image++, file)
    }

    return EXIT_SUCCESS;
}

static int read_image(struct image *image, FILE *file) {
    static long int expected_position = 4;

    if (ftell(file) != expected_position) {
        fprintf(stderr, "Reading @ %lu when should be @ %lu!",
                ftell(file), expected_position);
        exit(EXIT_FAILURE);
    }

    unsigned int width = read_int_from_file(file);
    unsigned int height = read_int_from_file(file);

    unsigned int size = width * height;

    unsigned char *data = malloc(sizeof(char) * size);
    if (data) {
        if (fread(data, sizeof(char), size, file) != size) {
            exit(EXIT_FAILURE);
        }

        image->width = width;
        image->height = height;
        image->data = data;

        expected_position += 2 * 4 + width * height;
        return EXIT_SUCCESS;
    } else {
        exit(EXIT_FAILURE);
    }
}

The problem is that the file pointer is sometimes going ahead when it shouldn't do so, i.e. I'm hitting ftell(file) != expected_position. I'm getting it after a good amount of successful reads, but some good time before the end, too.

Does anyone have any idea why that may be? I mean, even if the numbers were wrong, that shouldn't happen, should it? Thanks!

Upvotes: 0

Views: 1683

Answers (2)

Heath Hunnicutt
Heath Hunnicutt

Reputation: 19467

MS-DOS end-of-line sequences are Carriage Return, New Line (CR NL, 0x0D, 0x0A), and Unix uses simply New Line (NL or 0x0A).

Change the line

FILE *input_file = fopen(filename, "r");

to

FILE *input_file = fopen(filename, "rb");

Otherwise, the fread() function used to translate CR NL as NL, on Unix systems prior to POSIX standard "IEEE Std 1003.1-1988".

On MS-DOS, Windows and derivatives, it translates NL as CR NL.

These translations cause your opinion of the file position to differ from ftell()'s calculations.

Upvotes: 5

Kiril Kirov
Kiril Kirov

Reputation: 38183

You need to open the binary file as such:

//                                   v
FILE *input_file = fopen(filename, "rb");

Upvotes: 4

Related Questions