【问题标题】:Reading file and populating struct读取文件并填充结构
【发布时间】:2010-05-04 17:42:50
【问题描述】:

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

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

【问题讨论】:

  • 需要写入数据指针指向而不是它包含的地址。

标签: c file struct


【解决方案1】:

当你写你的对象时,你写的是指向文件的指针值而不是指向的信息。

你需要做的是只是fwrite/fread你的整个结构,而是一次做一个字段。 fwrite a 和 f 就像你对对象所做的那样,但是你需要对字符串做一些特殊的事情。尝试 fwrite/fread 的长度(未在您的数据结构中表示,这很好),然后 fwrite/fread 字符缓冲区。当然,在读取时,您需要分配它。

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 结构对齐怎么样?
    • 结构对齐呢?只要数据是由编写它的同一应用程序读取的,它应该不是问题。任何填充在写入时都将是未初始化的数据,并且在读取时可能会发生变化,但您并不指望它首先处于任何特定状态......
    • 如果您担心结构对齐,您的编译器很可能会选择“打包”您的结构(即不使用填充字节)。在 gcc 中,您可以在 struct 定义的右大括号之后添加 __attribute__((__packed__))。然而,正如 dash-tom-bang 指出的那样,如果您从同一个应用程序读取和写入数据,对齐应该不是问题。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

        【解决方案5】:

        结构的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 语言字符串。 优点:比 Size First 需要更少的空间。
        缺点:文本必须一次读取一个字节,这样终端字符就不会丢失。

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

        将数据结构写入文件时,您应该考虑使用数据库。有小的如 SQLite 和大的如 MySQL。不要浪费时间为您的数据编写和调试永久存储例程,因为它们已经并经过测试

        【讨论】:

        • @deostroll:我为第一种方法添加了代码。第二种方法可以使用标准库函数进行字符串 I/O。
        • 就像你之前提到的那样,我保留了一个存储字符串长度的字段(到 obj->a)...我已经将所有这些写入文件。但是我在尝试阅读时仍然遇到段错误> pastebin.com/UuDGVQxZ
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-24
        • 2019-01-12
        • 2010-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多