Должен ли я выполнять fseek несколько раз, если я хочу получить доступ к другой части информации в файле?

В фиксированных местах метаданных файла есть три важных целых числа, каждое из которых занимает ровно 4 байта:

По смещению 10–13 байтов — смещение в файле растрового изображения, с которого начинается массив пикселей.

При смещении 18-21 байт ширина изображения в пикселях.

При смещении 22-25 байт высота изображения в пикселях.

/*
 * Read in the location of the pixel array, the image width, and the image
 * height in the given bitmap file.
 */
void read_bitmap_metadata(FILE *image, int *pixel_array_offset, int *width, int *height) {

    fseek(image, 10, SEEK_SET);
    fread(pixel_array_offset, 4, 1, image);
    fseek(image, 18, SEEK_SET);
    fread(width, 4, 1, image);
    fseek(image, 22, SEEK_SET);
    fread(height, 4, 1, image);

}

Должен ли я использовать fseek 3 раза здесь, даже заметив, что ширина и высота непрерывны?


person user8314628    schedule 08.02.2019    source источник
comment
Указатель файла перемещается автоматически. Вам нужно только искать, если данные не находятся в текущем указателе файла (или, возможно, при переключении между r/w).   -  person Weather Vane    schedule 09.02.2019


Ответы (2)


Нет, вы можете опустить последний fseek().

fread() всегда перемещает позицию в файле на количество прочитанных данных. Таким образом, вам нужно fseek() только тогда, когда вы хотите пропустить несколько байтов или когда вы хотите перейти к фиксированной позиции, и вам все равно, где вы сейчас находитесь.

Поскольку вы пропускаете только несколько байтов, вы можете просто сделать один fread() из смещения 0 из 26 байтов в буфер, а затем выбрать данные из буфера по мере необходимости.

Предупреждение о порядке следования байтов. Существует одна большая проблема при чтении многобайтовых целых чисел из файла: работает это или нет, зависит от порядка следования байтов файла и узла. Ваш код работает только тогда, когда они совпадают. Если они не совпадают, вы должны поменять местами байты после операции fread(). В Linux для этого используется bswap_32(), или, если порядок байтов в файле big endian (также известный как сетевой порядок байтов), вы можете использовать ntohl().

person Johannes Overmann    schedule 08.02.2019
comment
Возможно, было бы еще эффективнее выполнить однократное чтение 26 байтов в буфер, а затем извлечь из памяти 3 интересные части. Вам все равно придется помнить о проблемах с порядком байтов. - person AShelly; 09.02.2019
comment
@ASHelly: полностью согласен. Спасибо. Я добавил эту подсказку. - person Johannes Overmann; 09.02.2019

Если вы используете современную ОС (у которой есть виртуальная память), вы можете использовать mmap, чтобы отобразить файл в память (не копировать, он использует виртуальную память). Это позволит вам читать/(опционально писать) и искать, используя операции с памятью: арифметика указателей/массивы, в C.

Для Unix (Gnu/Linux, MacOS, BSD, System V) см. https://en.wikipedia.org/wiki/Mmap и http://man7.org/linux/man-pages/man2/mmap.2.html

Я думаю, что даже Microsoft Windows теперь может это делать, см. https://docs.microsoft.com/en-gb/windows/desktop/Memory/file-mapping

person ctrl-alt-delor    schedule 08.02.2019
comment
Это не портативно; не могу запустить в винде - person Edward Karak; 09.02.2019