Чтение файла и заполнение структуры - PullRequest
4 голосов
/ 04 мая 2010

У меня есть структура со следующим определением:

typedef struct myStruct{
    int a;
    char* c;
    int f;
} OBJECT;

Я могу заполнить этот объект и записать его в файл. Однако я не могу прочитать значение char * c в нем ... при попытке прочитать это дает мне ошибку ошибки сегментации. Что-то не так с моим кодом:

//writensave.c

#include "mystruct.h"
#include <stdio.h>
#include <string.h>


#define p(x) printf(x)

int main()
{
    p("Creating file to write...\n");
    FILE* file = fopen("struct.dat", "w");
    if(file == NULL)
    {
        printf("Error opening file\n");
        return -1;
    }

    p("creating structure\n");
    OBJECT* myObj = (OBJECT*)malloc(sizeof(OBJECT));
    myObj->a = 20;
    myObj->f = 45;
    myObj->c = (char*)calloc(30, sizeof(char));
    strcpy(myObj->c, 
        "This is a test");
    p("Writing object to file...\n");
    fwrite(myObj, sizeof(OBJECT), 1, file);
    p("Close file\n");
    fclose(file);
    p("End of program\n");
    return 0;       
}

Вот как я пытаюсь это прочитать:

//readnprint.c
#include "mystruct.h"
#include <stdio.h>
#define p(x) printf(x)
int main()
{   
    FILE* file = fopen("struct.dat", "r");
    char* buffer;
    buffer = (char*) malloc(sizeof(OBJECT));
    if(file == NULL)
    {
        p("Error opening file");
        return -1;
    }

    fread((void *)buffer, sizeof(OBJECT), 1, file);
    OBJECT* obj = (OBJECT*)buffer;
    printf("obj->a = %d\nobj->f = %d \nobj->c = %s",
        obj->a,
        obj->f,
        obj->c);
    fclose(file);
    return 0;
}

Ответы [ 5 ]

2 голосов
/ 04 мая 2010

Когда вы пишете свой объект, вы записываете в файл значение указателя, а не указанную информацию.

То, что вам нужно сделать, это , а не , просто написать / перечитать всю структуру, а лучше сделать это поле за раз. Напишите a и f, как вы делаете с объектом, но тогда вам нужно сделать что-то особенное со строкой. Попробуйте fwrite / fread длины (не представленной в вашей структуре данных, это нормально), а затем fwrite / fread символьный буфер. При прочтении вам, конечно, нужно будет это выделить.

2 голосов
/ 04 мая 2010

Ваш первый пример кода предполагает, что строки будут не длиннее 30 символов. Если это так, то, вероятно, самое простое решение - это переопределить вашу структуру следующим образом:

typedef struct myStruct{
    int a;
    char c[30];
    int f;
} OBJECT;

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

1 голос
/ 04 мая 2010

Поле char * вашей структуры известно как поле переменной длины . Когда вы пишете это поле, вам понадобится метод определения длины текста. Два популярных метода:
1. Первый размер письма
2. Написание терминального символа

Первый размер письма
В этом методе сначала записывается размер текстовых данных, а затем сразу же данные.
Преимущества: текст может загружаться быстрее при чтении блока.
Недостатки: требуется два чтения, для данных длины требуется дополнительное пространство.
Пример кода:

struct My_Struct
{
   char * text_field;
};

void Write_Text_Field(struct My_Struct * p_struct, FILE * output)
{
  size_t text_length = strlen(p_struct->text_field);
  fprintf(output, "%d\n", text_length);
  fprintf(output, "%s", p_struct->text_field);
  return;
}

void Read_Text_Field(struct My_STruct * p_struct, FILE * input)
{
  size_t text_length = 0;
  char * p_text = NULL;
  fscanf(input, "%d", &text_length);
  p_text = (char *) malloc(text_length + sizeof('\0'));
  if (p_text)
  {
     fread(p_text, 1, text_length, input);
     p_text[text_length] = '\0';
  }
}

Запись терминального символа В этом методе текстовые данные записываются с последующим символом «терминал». Очень похоже на строку языка Си. Преимущества: Требуется меньше места, чем Size First.
Недостатки: текст должен читаться по одному байту за раз, чтобы не пропустить символ терминала.

Поле фиксированного размера
Вместо использования char* в качестве члена, используйте char [N], где N - максимальный размер поля. Преимущества: записи фиксированного размера можно читать как блоки. Облегчает произвольный доступ к файлам. Недостатки: потеря пространства, если все пространство поля не используется. Проблемы, когда размер поля слишком мал.

При записи структур данных в файл вам следует рассмотреть возможность использования базы данных . Есть маленькие, такие как SQLite, и большие, такие как MySQL. Не тратьте время на запись и отладку процедур постоянного хранения ваших данных, когда они уже записаны и протестированы .

1 голос
/ 04 мая 2010

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

Если файл данных должен использоваться совместно компьютерами разныхendian-ness, вам нужно будет сконфигурировать преобразователи файл-хост и хост-файл для типов не-char (int, short, long, long long, ...).Кроме того, было бы разумно использовать вместо этого типы из stdint.h (int16_t, int32_t, ...), чтобы гарантировать требуемый размер.

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

1 голос
/ 04 мая 2010

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

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