Сохранение структуры C со строкой char * в файл - PullRequest
7 голосов
/ 18 июля 2010

Я пытаюсь сохранить структуру со строкой char * в файл.

struct d_object {
    int flags;
    int time;
    int offset;
    char *filename;
};

Проблема в том, что при этом я, очевидно, сохраню только адрес этого указателя, а не строку. Итак, я просто использовал массив символов, но я вынужден установить максимальный размер строки. Это прекрасно работает, однако мне было интересно, есть ли в любом случае сохранение структуры с символом * (который я в некоторый момент использовал malloc) в файле и затем извлечение его. Я могу сохранить строку и структуру отдельно, а затем извлечь их, но это довольно беспорядок. Было бы предпочтительнее, если бы я мог загрузить и сохранить всю структуру (приведенную выше) в файл. Спасибо!

Код с массивом символов приведен ниже:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>

struct d_object {
    int flags;
    int time;
    int offset;
    char filename[255];
};

int main(int argc, char **argv) {

    struct d_object fcb;

    fcb.flags=5;
    fcb.time=100000;
    fcb.offset=220;
    strncpy(fcb.filename,"myfile",255);


    int fd=open("testfile",O_RDWR);
    write(fd,&fcb,sizeof(fcb));
    close(fd);


    int fd2 = open("testfile",O_RDONLY);
    struct d_object new_fcb; 
    read(fd2,&new_fcb,sizeof(new_fcb));

    printf("read from file testfile: %s\n",new_fcb.filename);

    return 0;

}

P.S .: Я не использую функции STREAM просто потому, что это на самом деле предназначено для запуска на встроенной ОС, у которой их нет. Я только что адаптировал код для * BSD / Linux, чтобы он имел больше смысла при задании вопроса.

Ответы [ 5 ]

7 голосов
/ 18 июля 2010

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

Вы можете преобразовать свой код обратно в:

struct d_object {
    int flags;
    int time;
    int offset;
    char * filename;
};

, а затем сохранить каждый фрагмент данных индивидуально:

write( fd, &record.flags, sizeof( int ) );
write( fd, &record.time, sizeof( int ) );
write( fd, &record.offset, sizeof( int ) );
int filename_length = strlen( filename );
write( fd, &filename_length, sizeof( int ) );
write( fd, record.filename, filename_length );

Для чтения вам придется читать каждый элемент отдельно, а затем имя файла:

int filename_length;

read( fd, &emptyRecord.flags, sizeof( int ) );
read( fd, &emptyRecord.time, sizeof( int ) );
read( fd, &emptyRecord.offset, sizeof( int ) );

read( filename_length, sizeof( int ), 1, file );
emptyRecord.filename = (char *) malloc( sizeof( char ) * ( filename_length  +1) );
read( fd, emptyRecord.filename, filename_length );
*( emptyRecord.filename + filename_length ) = 0;
2 голосов
/ 18 июля 2010

Теперь, когда я знаю природу вашей проблемы, почему бы не попробовать гибкие массивы? Вместо использования char *filename;, используйте char filename[1] и malloc(sizeof struct d_object + filename_len) для выделения своих структур. Добавьте элемент размера, и вы можете легко записать объект на диск с помощью одного вызова write и загрузить его с диска с помощью 2 вызовов (сначала для чтения элемента размера, затем для чтения всего объекта после его выделения).

Обратите внимание, что "официальным" способом создания гибких массивов в C99 является [], а не [1], но [1] гарантированно будет работать как следствие других требований стандарта и работает также на C89. [1] будет тратить несколько байтов, поэтому, если ваш компилятор поддерживает [], вы можете использовать его.

2 голосов
/ 18 июля 2010

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

Вместо этого вам следует разработать формат для ваших данных на диске и записать функции для сохранения и загрузки данных, либо использовать существующий формат и найти код библиотеки для использования.Это.Обычно это называется «сериализацией».

Если ваши данные являются иерархическими, JSON, XML или EBML могут быть подходящими.Если это довольно простые текстовые файлы или самодельный двоичный формат (записанный побайтово, так что он переносим), то может быть уместным.

Поскольку вы, кажется, не знакомы с этими проблемами, возможно, стоит написатьнекоторый код для загрузки / сохранения нескольких простых двоичных форматов файлов (таких как .tga или .wav) в качестве упражнения перед попыткой создать что-то для ваших собственных данных.

2 голосов
/ 18 июля 2010

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

void write_object(struct d_object *s, int fd) {
    struct d_object copy = *s;
    copy.filename = (char*)strlen(s->filename);
    write(fd, &copy, sizeof(copy));
    write(fd, s->filename, (size_t)copy.filename);
}

void read_object(struct d_object *s, int fd) {
    read(fd, s, sizeof(struct d_object));
    char *filename = malloc(((size_t)s->filename) + 1);
    read(fd, filename, (size_t)s->filename);
    filename[(size_t)s->filename] = '\0';
    s->filename = filename;
}
1 голос
/ 18 июля 2010

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

Что вам нужно сделать, так это написать пару функций для преобразования ваших данных в форму для записи и считывания их обратно из файла. Это называется «сериализация» / «десериализация» в языках, которые больше суетятся об этом.

Ваша конкретная структура, вы можете сделать что-то вроде записи двоичного кода в файл прямо из структуры, а затем дополнить его содержимым буфера символов. Вы могли бы упростить для себя время чтения, если перед символьными данными указывается int, указывающий их длину.

Когда вы прочитаете этот материал обратно, вы захотите malloc / calloc себе кусок памяти для хранения данных char; если вы сохранили размер, вы будете знать, насколько велика эта маллок. Считайте двоичные данные в структуру, прочитайте данные char в память malloc, сохраните указатель на кусок malloc в структуре, и вы заново создали оригинал.

Без магии. Только данные и немного локоть смазки.

EDIT

Пока я писал об этом, Томас привел пример. Я думаю, что наши ответы очень хорошо дополняют друг друга; вместе они должны рассказать вам все, что вам нужно знать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...