чтение большого bin-файла (~ 2 МБ) в c

#c #file #bin

#c #файл #bin

Вопрос:

Я хочу прочитать bin-файл размером менее 2 МБ.

На данный момент мой код для чтения bin-файла выглядит следующим образом:

Редактировать:

 #define MAX_BYTES_IN_FILE 500000         // ~ 2mb
#define ERROR_FILE 1

int get_byte_from_file(FILE *stream, unsigned char *dataarray) {
    int counter = 0;
                               
    while ((dataarray[counter] = fgetc(stream)) != EOF) {
        counter  = 1;
    }
    return counter;
}
  

Main выглядит так для примера использования функции.

 int main(int argc, char **argv) {
    FILE *datei;
   
    unsigned int number_of_bytes;
    unsigned char *dataarray;

    dataarray = (unsigned char *)malloc(sizeof(unsigned char) * MAX_BYTES_IN_FILE);

    datei = fopen(argv[1], "rb");
   
    number_of_bytes = get_byte_from_file(datei, dataarray);
   
    for (int i = 0; i < number_of_bytes; i  )
        printf("%x ", dataarray[i]);
   
    return 0;
}
  

Возможно, я допустил простую ошибку, но не могу ее увидеть, ошибка все еще: Segmentation fault (core dumped)

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

1. Этот массив может быть слишком большим для размещения в стеке. Не уверен, для чего вам это вообще нужно

2. Хммм. Этот массив совершенно бесполезен. Вы используете его только для чтения одного символа и немедленного присвоения его dataarray[counter] . Почему бы вместо этого не заменить его одним char ? Или используйте dataarray в качестве буфера для чтения данных (не по 1 байту за раз, а большими порциями) непосредственно в этот буфер

3. В Microsoft Windows размер стека по умолчанию равен 1 МБ. В Linux размер стека по умолчанию составляет (я полагаю) 8 МБ. Однако эти значения могут быть настроены. Таким образом, ваша программа, скорее всего, сбой из-за переполнения стека , потому что локальные переменные / массивы хранятся в стеке. По этой причине я предлагаю вам вместо этого использовать динамическое выделение памяти, например, функцию malloc .

4. Может быть еще одна причина ошибки сегментирования. Откуда вы знаете, насколько велик буфер, на который dataarray указывает точка? Помимо сбоя вашего стека, вы также можете записывать за пределы при копировании символа в этот буфер.

5. Ваш измененный код также неверен: fgetc возвращает int , а не unsigned char . Как unsigned char вообще можно было бы сохранить EOF ?

Ответ №1:

Этой строки достаточно для сбоя вашей программы:

 while ((dataarray[counter] = fgetc(stream)) != EOF) {
  

Давайте пройдемся по нему шаг за шагом:

  1. fgetc(stream) считывает байт и возвращает его значение или EOF . Поскольку байт может иметь любое возможное значение, fgetc() возвращает большее int значение, которое может содержать EOF значение, отличное от любого значения байта, которое может быть найдено в файле.

  2. Вы присваиваете это int значение unsigned char . EOF Значение будет урезано до этого типа данных.

  3. Значение присваивания имеет тип unsigned char , и преобразованное EOF значение больше не равно EOF . Таким образом, сравнение всегда завершается неудачей, и ваша программа продолжает извлекать данные до тех пор, пока буфер не переполнится и не начнут происходить неприятные вещи.

Вам нужно сохранить результат fgetc() в int переменной, пока вы не убедитесь, что это действительно не EOF значение.

Ответ №2:

Может быть, что-то вроде этого.

 void *readfile(FILE *fi, long *filesize)
{
    void *buff;
    fseek(fi, 0, SEEK_END);
    *filesize = ftell(fi);
    fseek(fi, 0, SEEK_SET);
    buff = malloc(*filesize);
    if(buff)
    {
        fread(buff, 1, *filesize, fi);
    }
    return buff;
}
  

Вам нужно добавить проверки на ошибки — я этого не делал, поскольку это только идея.

И ваше использование:

 int main(int argc, char **argv) {
 
   FILE *datei;
   
   long number_of_bytes;
   unsigned char *dataarray;

   datei=fopen(argv[1],"rb");
   
   dataarray = readfile(datei, amp;number_of_bytes);
   
   for (int i=0;dataarray amp;amp; i<number_of_bytes;i  )
       printf("%hhx ",dataarray[i]);
   
   return 0;
}
  

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

1. Это бесполезно, когда вы представляете только рабочий код в качестве ответа и никогда не объясняете, что OP сделал неправильно. Таким образом, они не могут учиться, они только становятся вуду-кодерами, которые зависят от StackOverflow для исправления своих кодов.

2. @cmaster-переустановите monica Не только это, код не обязательно будет работать. Он абсолютно не проверяет ошибки, и использование fseek() / ftell() для определения размера файла в потоке может привести к сбою многими способами — чтение из канала fseek() до конца двоичного потока в некоторых системах не работает, в Windows, где long всего 32 бита, если произойдет сбой для файлов размером 2 ГБ или больше, и т.д.

3. long возможно, недостаточно большой, чтобы вместить размер файла.

4. @AndrewHenle OP читает файл ~ 2 МБ. Достаточно долго. Для целей операций fseek / ftell достаточно

5. @P__J__ Для целей операций fseek / ftell достаточно хорош До тех пор, пока этого не произойдет. Ошибочный код — это ошибочный код, даже если ошибка не вызвана.

Ответ №3:

Причина, по которой вы получаете ошибку сегментации, заключается в неправильном распределении: вы выделяете MAX_BYTES_IN_FILE байты вместо unsigned int элементов. При распределении массив содержит только MAX_BYTES_IN_FILE / sizeof(unsigned int) элементы, тогда как файл, вероятно, имеет длину MAX_BYTES_IN_FILE * sizeof(unsigned int) байт.

Вы считываете байты из файла (значения между 0 и 255 ), но используете unsigned int элементы. В чем логика? Содержит ли файл 32-разрядные значения или отдельные байты?

Как только вы сможете подтвердить, что содержимое файла точно совпадает с представлением массива в памяти, вы можете использовать fread() для чтения всего файла за один вызов.