Как правильно прочитать определенное количество байт в структуру?

У меня есть структура:

struct map {
   void* key;
   void* value;
}

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

void* buffer = malloc(size);

fseek(file, 0, SEEK_SET);

fread(buffer, size, 1, file);

И мне нужно определенное количество байт из буфера замапить на структуру. Т.е. допустим достать 40 байт из буфера, 20 байт в поле key и 20 в value, и так пройти по всему буферу и вернуть как массив map'ов. Собственно вопрос: Как правильно прочитать 20 байт в key и 20 байт в value?


Ответы (4 шт):

Автор решения: Harry

Нельзя просто так взять и считать указатели. Вам нужно записывать каждую структуру с указанием размеров, примерно как

map m;
...
int len = strlen(m.key);
fwrite(&len,sizeof(len),1,file);
fwrite(m.key,len+1,1,file);
len = strlen(m.value);
fwrite(&len,sizeof(len),1,file);
fwrite(m.value,len+1,1,file);

а потом, при чтении, читать поочередно...

map m;
...
int len;
fread(&len,sizeof(len),1,file);
m.key = malloc(len);
fread(m.key,len+1,1,file);
fread(&len,sizeof(len),1,file);
m.value = malloc(len);
fread(m.value,len+1,1,file);

Примерно так.

Иначе вы просто записываете и читаете указатели, а не данные, на которые они указывают.

Ели бы вы описали структуру как

struct map {
   char key[20];
   char value[20];
};

тогда дело другое, можно читать-писать непосредственно. Но в указанном вами варианте это невозможно.

→ Ссылка
Автор решения: Yaroslav

Можно реализовать вот так

struct map
{
  uint8_t key[40];
  uint8_t value[40];
}__attribute__((packed));

uint8_t * buffer = malloc(size);
fread(buffer, size, 1, file);

map * begin_map = (map*)buffer;
map * current_map = begin_map;
map * end_map = (map*)(bufffer + size);

/*теперь к поля структуры можно обращаться  */
current_map->key;
current_map->value;
/*перемещаться между структурами */ 
current_map += number;
current_map -= number;
/*но нужно проверять не вышли за пределы данных*/
if (current_map > begin_map) {
   ...
}
if (current_map < end_map) {
   ...
}
→ Ссылка
Автор решения: Yakov

Примерно так. если я правильно все понял. ногами не бейте. писал в блокноте по памяти. может где ошибся.

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

typedef 
struct 
{
    union
    { 
        char[20] key; 
        void* pkey;
    } 
    union
    { 
        char[20] value; 
        void *pvalue;
    }
} MYMAP, *PMYMAP;

void main()
{
    void* buffer;
    int i;
    long size;
    fpos_t *pos; /* fpos_t определен в stdio.h */
    pos = &l;
    FILE *file = fopen("data.txt", "r");
    fseek(file, 0, SEEK_END);
    fgetpos(file, (fpos_t*) &size); 
    fseek(file, 0, SEEK_SET);
    buffer = malloc(size+2);
    fread(buffer, size,  size, file);
    //после чтения файла в буфер просто делим его на куски. 
    for(i=0;i<size/sizeof(MYMAP);i++)
    {
        //т.к. это пример, положим, что значения key и value в файле завершаются нолем и будут выводиться как си-строка. если нет эту часть надо исправить.
        printf("key = %s, value = %s", 
        PMYMAP(buffer+i*sizeof(MYMAP)).key,
        PMYMAP(buffer+i*sizeof(MYMAP)).value);
    }
}
→ Ссылка
Автор решения: avp

Допустим, судя по имени структуры (map), что предполагается именно маппинг, т.е. доступ к данным непосредственно в буфере.

Тогда, если использовать вашу структуру с указателями, то помимо буфера надо еще выделить память под массив структур, далее заполнить его адресами в буфере и вернуть.

Получим что-то вроде

struct map * 
foo (FILE *f, size_t size)
{
   char *buffer = malloc(size); // предположим, что size в самом деле кратен 40 и проверять это не будем
   struct map *p = malloc(size / 40 * sizeof(struct map));

   rewind(f);
   fread(buffer, size, 1, f);

   size_t i = 0;
   for (char *t = buffer; t < buffer + size; t += 40, i++) {
      p[i].key = t; p[i].value = t + 20;
   }

   return p;
}

По окончании обработки удаляете буфер

struct map *m_map = foo(input_file, data_size);

.....
  for (int i = 0; i < data_size / 40; i++)
    printf("key: %s  value: %s\n", m_map[i].key, m_map[i].value);
.....

 free (m_map[0].key);

и потом map (полученный массив структур)

 free (m_map);

Если же хотите работать непосредственно с данными в буфере, то надо использовать вот такую структуру

struct c_map {
  char key[20];
  char value[20];
};

Тогда получаем вот такую функцию для чтения даннных

 struct c_map *
 bar (FILE *f, size_t size)
 {
    char *buffer = malloc(size); // предположим, что size в самом деле кратен 40 и проверять это не будем

    rewind(f);
    fread(buffer, size, 1, f);

    return (struct c_map *)buffer;
 }

Получаете массив отмапленных на буфер структур и по окончании обработки удаляете буфер вот так

 struct c_map *m_map = bar(input_file, data_size);
 .....
  for (int i = 0; i < data_size / 40; i++)
    printf("key: %s  value: %s\n", m_map[i].key, m_map[i].value);
 .....
 free(m_map);

Конечно, в обеих вариантах, если после каждых 19 байт в файле не будет двоичного нуля (терминатора строки), то и в полях структуры он не появится.

→ Ссылка