Как правильно прочитать определенное количество байт в структуру?
У меня есть структура:
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 шт):
Нельзя просто так взять и считать указатели. Вам нужно записывать каждую структуру с указанием размеров, примерно как
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];
};
тогда дело другое, можно читать-писать непосредственно. Но в указанном вами варианте это невозможно.
Можно реализовать вот так
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) {
...
}
Примерно так. если я правильно все понял. ногами не бейте. писал в блокноте по памяти. может где ошибся.
#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);
}
}
Допустим, судя по имени структуры (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 байт в файле не будет двоичного нуля (терминатора строки), то и в полях структуры он не появится.