использование fwrite и fread для сохранения и извлечения примера базы данных malloc

gcc (GCC) 4.7.0
c89

Привет,

Я пытаюсь сохранить базу данных строк в файл (db.dat), используя fwrite, чтобы записать ее и получить с помощью fread. Однако, когда я извлекаю его с помощью fread, данные пусты.

Я не уверен, должен ли я выделить немного памяти перед получением данных. Что я пробовал в своем примере

Я не показываю здесь свободную память, так как это просто фрагменты кода.

Большое спасибо за любые предложения,

This is my structure of the database:
struct address {
    int id;
    int set;
    char *name;
    char *email;
};

struct database {
    struct address **rows;
    size_t data_size;
    size_t max_size;
};

struct connection {
    FILE *fp;
    struct database *db;
};

База данных открыта:

struct connection* database_open(const char *filename, char mode)
{
    struct connection *conn = NULL;

    conn = malloc(sizeof(struct connection));

    conn->db = malloc(sizeof(struct database));
    if(conn->db == NULL) {
        log_exit("Memory error allocating database", conn);
    }

    if(conn == NULL) {
        log_exit("Memory error allocating connection", conn);
    }

    printf("Create a new database\n");
    conn->fp = fopen(filename, "w");

    if(conn->fp == NULL) {
        log_exit("Failed to open file", conn);
    }

    return conn;
}

создание базы данных:

void database_create(struct connection *conn, int _max_size, int _data_size)
{
    int i = 0;
    struct address *db_row = NULL;

    /* Set the size of the database by assigning the max number of database rows */
    conn->db->max_size = _max_size;
    conn->db->data_size = _data_size;

    /* Allocate memory for rows and size */
    conn->db->rows = malloc(sizeof(struct address) * _max_size);
    if(!conn->db->rows) {
        log_exit("Memory error alocating rows", conn);
    }

    for(i = 0; i < _max_size; i++) {
        db_row = malloc(sizeof(struct address));

        db_row->id = i;
        db_row->set = 0;
        db_row->name = malloc(_data_size);
        db_row->email = malloc(_data_size);

        /* Add the row to the database */
        conn->db->rows[i] = db_row;
    }
}

Написание базы данных

void database_write(struct connection *conn)
{
    rewind(conn->fp);

    if(fwrite(conn->db, sizeof(struct database), 1, conn->fp) != 1) {
        log_exit("Failed to write to database.", conn);
    }

    if(fflush(conn->fp) != 0) 
    {
        log_exit("Cannot flush database.", conn);
    }
}

Затем я открываю базу данных для r+ и использую fread. Однако мои данные, которые я установил в переменных id и set, пусты.

void database_load(struct connection *conn)
{
    /* Clear any errors from file pointer */
    clearerr(conn->fp);

    /* I have had to hack this just so that I can get the
       memory allocated before filling the structure not sure if this is the best way */
    conn->db->rows = malloc(sizeof(struct address) * 1); /* This would be set to how many rows I have - just experimenting with just one */
    conn->db->rows[0] = malloc(sizeof(struct address));
    conn->db->rows[0]->name = malloc(20);
    conn->db->rows[0]->email = malloc(20);

    if(fread(conn->db, sizeof(struct database), 1, conn->fp) != 1) {
        if(feof(conn->fp) != 0) {
            log_exit("Database end of file", conn);
        }

        if(ferror(conn->fp) != 0) {
            log_exit("Database cannot open", conn);
        }
    }    
}

==== РЕДАКТИРОВАТЬ ===

int main(int argc, char **argv)
{
    char *filename = NULL;
    char action = 0;
    int id = 0;
    int rows = 0;
    int size = 0;
    struct connection *conn = NULL;

    if(argc < 3) {
        log_exit("USAGE: ex17 <dbfile> <action> [ action params ]", conn);
    }

    filename = argv[1];
    action = argv[2][0];
    rows = atoi(argv[3]);
    size = atoi(argv[4]);

    conn = database_open(filename, action);

    if(argc > 3) {
        id = atoi(argv[3]);
    }

    switch(action) {
    case 'c':
        database_create(conn, rows, size);
        database_write(conn);
        break;

    case 's':
        if(argc != 6) {
            log_exit("Need id, name, email to set", conn);
        }

        database_set(conn, id, argv[4], argv[5]);
        database_write(conn);
        break;

    default:
        log_exit("Invalid action, only: c = create, g = get, d = del, l = list", conn);
    }

    database_close(conn);

    return 0;
}

person ant2009    schedule 21.09.2012    source источник
comment
Действительно ли database_set правильно устанавливает значения?   -  person Tony The Lion    schedule 21.09.2012
comment
Я не могу зайти так далеко. Так как мне нужно открыть базу данных с помощью файла fread. Поскольку я не могу этого сделать, я не могу двигаться дальше с этой проблемой. Спасибо.   -  person ant2009    schedule 21.09.2012
comment
Извините, но вам нужно сначала записать в свой файл, прежде чем вы сможете прочитать его. Кроме того, я нигде не вижу, чтобы ваш database_load вызывался в вашем коде, поэтому я не могу сказать, где вы на самом деле читаете.   -  person Tony The Lion    schedule 21.09.2012
comment
Это проблема сериализации. Когда вы вызываете fwrite для записи conn->db, вы записываете адрес, хранящийся в rows, в файл, а не сами фактические структуры. Вам нужно сохранить количество элементов rows в файл, чтобы вы знали, сколько их нужно прочитать позже, а затем циклически сохраняйте каждую структуру. Вы также должны иметь дело с указателями char * внутри структур, сохраняя их длины, а затем записывая их данные, иначе вы снова будете просто сохранять адреса памяти в файл, а не фактические данные.   -  person Kludas    schedule 21.09.2012


Ответы (1)


Видя, что ваш rows является двойным указателем, вам нужно инициализировать первый указатель с помощью sizeof(pointer*), это:

conn->db->rows = malloc(sizeof(struct address) * 1); 

должно быть:

conn->db->rows = malloc(sizeof(struct address*));  //notice how struct address becomes struct address*

То же самое с этим:

conn->db->rows = malloc(sizeof(struct address) * _max_size);

должно быть:

  conn->db->rows = malloc(sizeof(struct address*) * _max_size);
person Tony The Lion    schedule 21.09.2012
comment
После внесения этих изменений. У меня все еще та же проблема. set и id по-прежнему обнуляются после fread. Ничего особенного не сделал. Спасибо. Идентификатор будет увеличиваться в зависимости от количества строк, которые я хочу. Однако сделать это не удалось. Большое спасибо за любые дополнительные предложения. - person ant2009; 21.09.2012
comment
Вы прошли через это с отладчиком? - person Tony The Lion; 21.09.2012
comment
Вы создаете такое же количество строк для обратного чтения, сколько вы записали в файл базы данных в первую очередь? - person Tony The Lion; 21.09.2012
comment
Я прошел через gdb. После возврата fread проверьте содержимое: (gdb) p *conn-›db-›rows[0] $35 = { id = 0, set = 0, name = 0x6022d0, email = 0x6022f0 Я установил для параметра set значение 10 и тогда fпишите это. Когда я это читаю. Это ноль. Не уверен, что там происходит. Однако возможно ли использовать fread и fwrite с памятью malloc. Я отредактировал свой вопрос с помощью моей основной функции. Спасибо. - person ant2009; 21.09.2012