读取文件和填充结构

我有一个结构具有以下定义:

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; 

否则,您只是存储一个指向动态分配的内存的指针,当您的程序退出时将被销毁(因此,当您稍后检索此指针时,该地址毫无价值且很可能非法访问)。

您正在保存指向char的指针,而不是字符串本身。 当您尝试重新加载文件时,您正在一个具有不同地址空间的新进程中运行,并且该指针不再有效。 您需要按值保存字符串。

我想添加一个关于潜在可移植性问题的说明,根据数据文件的计划使用情况,该问题可能存在也可能不存在。

如果要在不同端点的计算机之间共享数据文件,则需要为非char类型配置文件到主机和主机到文件转换器(int,short,long,long long,.. )。 此外,谨慎使用stdint.h中的类型(int16_t,int32_t,…)来保证所需的大小。

但是,如果数据文件不会在任何地方移动,则忽略这两点。

结构的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'; } } 

写入终端字符在此方法中,将写入文本数据,后跟“终端”字符。 非常类似于C语言字符串。 优点:比尺寸优先需要更少的空间。
缺点:文本必须一次读取一个字节,因此不会遗漏终端字符。

固定大小字段
不使用char*作为成员,而是使用char [N] ,其中N是字段的最大大小。 优点:固定大小的记录可以作为块读取。 使文件中的随机访问更容易。 缺点:如果不使用所有的场地空间,浪费空间。 字段大小太小时出现问题。

将数据结构写入文件时,应考虑使用数据库 。 有一些小的,比如SQLite,还有较大的,比如MySQL。 在编写和测试数据时,不要浪费时间为数据编写和调试永久存储例程。