读取文件和填充结构

时间:2010-05-04 17:42:51

标签: c file struct

我的结构具有以下定义:

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 个答案:

答案 0 :(得分:2)

当您编写对象时,您将指针值写入文件而不是指向的信息。

你需要做的是只是fwrite / fread你的整个结构,而是一次做一个字段。当你对对象进行fwrite a和f时,你需要对字符串做一些特别的事情。尝试fwrite / fread的长度(未在数据结构中表示,没关系),然后fwrite / fread字符缓冲区。在阅读时,你当然需要分配它。

答案 1 :(得分:2)

您的第一个代码示例似乎假设字符串不会超过30个字符。如果是这种情况,那么最简单的修复方法可能是重新定义您的结构:

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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

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

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

答案 4 :(得分:1)

您的结构的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。当数据已经编写并经过测试时,不要浪费时间为数据编写和调试永久存储例程。