Запись «упакованных» структур в файл с помощью C - PullRequest
3 голосов
/ 11 августа 2010

Как я могу "упаковать" и "записать" структуру в файл, используя C, чтобы:

struct a {
    uint64_t a;
    char* b;
    uint16_t c;
} a;
a b;
b.a = 3;
b.b = "Hello";
b.c = 4;

записывалось в файл как

00 00 00 00 00 00 00 03 48 65 6c 6c 6f 00 00 04

Ответы [ 4 ]

9 голосов
/ 11 августа 2010

В C вам придется написать функцию, которая сделает это за вас.Вы не можете просто записать структуру на диск, потому что b - это указатель, который не имеет смысла без вспомогательной строки.И, если вы не знаете (и не можете контролировать), как ваш компилятор упаковывает свои структуры, вам все равно лучше использовать служебную функцию, даже без указателей.

И, как будто этого недостаточно, вам следуеттакже выведите длину строки, чтобы вы знали, сколько байтов нужно прочитать обратно.

Вы будете искать что-то вроде:

int better_than_blat (FILE *f, struct a *x) {
    size_t len = strlen (x->b);
    if (fwrite (&(x->a), sizeof(long), 1, f) != 1) return -1;
    if (fwrite (&len, sizeof(size_t), 1, f) != 1) return -1;
    if (fwrite (x->b, len, 1, f) != 1) return -1;
    if (fwrite (&(x->c), sizeof(short), 1, f) != 1) return -1;
    return 0;
}

int better_than_unblat (FILE *f, struct a *x) {
    size_t len;
    if (fread (&(x->a), sizeof(long), 1, f) != 1) return -1;
    if (fread (&len, sizeof(size_t), 1, f) != 1) return -1;
    x->b = malloc (len + 1);
    if (x->b == NULL) return -1;
    memset (x->b, 0, len + 1);
    if (fread (x->b, len, 1, f) != 1) return -1;
    if (fread (&(x->c), sizeof(short), 1, f) != 1) return -1;
    return 0;
}
2 голосов
/ 11 августа 2010

вы должны написать свой собственный способ сериализации этих данных; компилятор не предоставит вам встроенный способ работы со строкой. Существуют библиотеки сериализации, но я не знаю ни одной для прямой C.

Но рассмотрите возможность использования более структурированного метода для сериализации данных, такого как json или xml. Даже INI-файл лучше, чем простой двоичный дамп. Причины этого:

  • проще для отладки
  • более совместимый с прямой пересылкой
  • менее жесткий / подвержен ошибкам
  • если вы используете существующую библиотеку, то вы пожинаете плоды сообщества, которое ее поддерживает.
  • существующая библиотека, скорее всего, будет поддерживать функции, которые вы позже поцарапаете, как массивы
  • лучшая кроссплатформенная совместимость.
0 голосов
/ 11 августа 2010

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

Например (gcc):

struct a {
    long a;
    char b[256];
    short c;
}  __attribute__((__packed__));

int size = sizeof(a);
void* buffer = malloc(size);
memcpy(buffer, (void*)a, size);
0 голосов
/ 11 августа 2010

Поможет ли следующее?

struct buffer {

  char bytes[1000];
  int nbytes;
};

struct buffer *new_buffer(){
  struct buffer b = (struct buffer*) malloc(sizeof(struct buffer));
  b->nbytes = 0;
  return b;
}

void append_long(struct buffer *b, long *l){
  memcpy(b->bytes + b->nbytes, l);
  b->nbytes += sizeof(*l);
}

// ...and so on for other types

void fwrite_buffer(FILE *fp, struct buffer *b){
  fwrite(b->bytes, sizeof(*b), 1, fp);
}

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

struct buffer *buf = new_buffer();
struct a b;
b.a = 3;
b.b = "Hello";
b.c = 4;
append_long(buf, &(b.a));
append_pointer(buf, &(b.b));
append_short(buf, &(b.b));
fwrite_buffer(fp, buf);
...