Как fread справляется с конечным мусором при достижении конца файла? - PullRequest
1 голос
/ 06 мая 2020

Недавно я снова начал пробовать C, язык, которым я не особенно владею и, по сути, постоянно забываю (я в основном кодирую Python). Моя идея состоит в том, чтобы читать данные из гипотетически большого файла в виде фрагментов, а затем обрабатывать данные соответствующим образом. На данный момент я моделирую это, фактически загружая весь файл в буфер типа short с помощью fread. Этот метод будет изменен, так как я бы подумал, что это будет очень плохая идея, скажем, для файла размером 1 ГБ. Конечная цель - прочитать фрагмент как один, обработать, переместить курсор, прочитать другой фрагмент и т. Д.

Размер рассматриваемого файла составляет 43 байта, и в нем есть фраза «Быстрая коричневая лисица прыгает через ленивого». собака". Этот размер удобен, потому что это простое число, поэтому независимо от того, на сколько байтов я его разделю, всегда будет мусор в конце (из-за того, что в буфере осталось свободное место?). Обработка данных в этом случае - это просто распечатка коротких строк в виде двух символов после манипуляции с байтами (см. Код ниже)

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

#define MAX_BUFF_SIZE 1024

long file_size(FILE *f) 
{
    if (fseek(f, 0, SEEK_END) != 0) exit(EXIT_FAILURE);     // Move cursor to the end
    long file_size = ftell(f);                              // Determine position to get file size
    rewind(f);

    return file_size;
}

int main(int argc, char* argv[])
{
    short buff[MAX_BUFF_SIZE] = {0};                        // Initialize to 0 remove trailing garbage
    char* filename  = argv[1];
    FILE* fp = fopen(filename, "r");

    if (fp) 
    {
        size_t size = sizeof(buff[0]);                      // Size in bytes of each chunk. Fixed to 2 bytes
        int nmemb = (file_size(fp) + size - 1) / size;      // Number of chunks to read from file
                                                            // (ceil f_size/size)
        printf("Should read at most %d chunks\n", nmemb);           

        short mask = 0xFF;                                  // Mask to take first or second byte

        size_t num_read = fread(buff, size, nmemb, fp); 

        printf("Read %lu chunks\n\n", num_read);            // Seems to have read more? Look into.

        for (int i=0; i<nmemb; i++) {
            char first_byte = buff[i] & mask;
            char second_byte = (buff[i] >> 8) & mask;       // Identity for 2 bytes. Keep mask for consistency

            printf("Chunk %02d: 0x%04x | %c %c\n",          // Remember little endian (bytes reversed) 
                    i, buff[i], first_byte, second_byte);         
        }
        fclose(fp);
    } else 
    {
        printf("File %s not found\n", filename);
        return 1;
    }
    return 0;
}

Теперь, вчера, при распечатке последнего фрагмента данных, который я получил, «Chunk 21: 0xffff9567 | г ". Последний (первый?) Байт (0x67) - это g, и я ожидал некоторого мусора в конце, но я не понимаю, почему он печатал так много байтов, когда в переменной buff есть шорты. В тот момент я просто печатал шестнадцатеричный код как% x, а не% 04x, и buff не был инициализирован значением 0. Сегодня я решил инициализировать его значением 0, и не только исчез мусор, но и я. не могу воссоздать проблему даже после того, как оставил buff снова неинициализированным.

Итак, вот мои вопросы, которые, надеюсь, не слишком абстрактны:

  1. выглядит ли fread за пределами файла при чтении данных и удаляет ли он сам конечный мусор, или это зависит от нас?
  2. Почему printf показывает int, когда буфер короткий? (Я предполагаю, что% x предназначен для целых чисел), и почему я не могу воспроизвести поведение даже после того, как оставил buff без инициализации?
  3. Должен ли я всегда инициализировать буфер равным нулю, чтобы удалить завершающий мусор? Каков здесь обычный подход?

Надеюсь, это не слишком много или слишком расплывчатых вопросов, и я был достаточно ясен. Как я уже сказал, я мало знаю о C, но нахожу программирование низкого-среднего уровня очень интересным, особенно когда дело доходит до прямого манипулирования битами / байтами данных.

Надеюсь, у вас отличный день!

РЕДАКТИРОВАТЬ 1: Некоторые из вас мудро предложили использовать num_read вместо nmemb на l oop, так как это возвращаемое значение fread, но это означает, что я Отбросим остальную часть файла ( nmemb равно 22, а num_read равно 21). Это обычный подход? Кроме того, спасибо за то, что вы указали, что% x приводил к беззнаковому int, следовательно, 4 байта вместо 2.

EDIT 2: для пояснения, и, поскольку я ошибся в комментарии, я хотел бы сохранить оставшийся байт (или данные), отбрасывая остальные, которые не определены. Я не знаю, является ли это обычным подходом, поскольку, если я использую num_read в l oop, все, что остается в конце, отбрасывается, данные или нет. Мне больше интересно знать, каков обычный подход: отбросить оставшиеся данные или удалить все, что, как мы знаем, не определено, в данном случае один из байтов.

...