Чтение файла и заполнение структуры

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

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

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

 //writensave.c #include "mystruct.h" #include  #include  #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  #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; } 

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

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

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

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

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

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

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

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

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

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

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

 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'; } } 

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

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

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