Чтение двоичного файла в C: ftell возвращает результаты, которые иногда отклоняются на единицу

#c #binary

#c #двоичный

Вопрос:

Я пытаюсь прочитать двоичный файл в следующем формате:

количество изображений [4-байтовый int]

width [4-байтовый int]
height [4-байтовый int]
данные в оттенках серого [ширина * высота в байтах]
(больше элементов того же типа)

Это первая вызываемая функция:

 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)
}
  

Вот image структура для тех, кому интересно:

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

И это то, что read_int_from_file делает:

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

Это остальное:

 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);
    }
}
  

Проблема в том, что указатель на файл иногда перемещается вперед, когда он не должен этого делать, т. Е. Я нажимаю ftell(file) != expected_position . Я получаю это после большого количества успешных чтений, но также и за некоторое время до конца.

У кого-нибудь есть какие-либо идеи, почему это может быть? Я имею в виду, даже если цифры были неправильными, этого не должно было произойти, не так ли? Спасибо!

Ответ №1:

Последовательности конца строки MS-DOS — это возврат каретки, новая строка ( CR NL , 0x0D, 0x0A ), а Unix использует просто новую строку ( NL или 0x0A ).

Измените строку

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

Для

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

В противном случае, fread() функция, используемая для перевода CR NL как NL , в системах Unix до стандарта POSIX «IEEE Std 1003.1-1988».

В MS-DOS, Windows и производных от них это переводится NL как CR NL .

Из-за этих переводов ваше мнение о позиции файла отличается от ftell() вычислений.

Комментарии:

1. Это сделало это. Спасибо! Очень глупо с моей стороны забывать, что это может быть так.

2. Я не понимаю ваш предпоследний абзац. В Unix абсолютно никакого перевода не происходит, поскольку стандарт POSIX требует, чтобы текстовый режим вел себя идентично двоичному режиму.

3. Спасибо за обновление. Я использую open() вместо fopen() с 1988 года.

4. Провел 3 дня, задаваясь вопросом, почему это не работает, и не смог понять, что я забыл добавить ‘b’ в fopen. Большое спасибо.

Ответ №2:

Вам нужно открыть двоичный файл как таковой:

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