将带有char *字符串的C结构保存到文件中

时间:2010-07-18 09:25:57

标签: c file file-io struct

我正在尝试将带有char *字符串的结构保存到文件中。

struct d_object {
    int flags;
    int time;
    int offset;
    char *filename;
};

问题在于,当我这样做时,我显然只会保存该指针的地址而不是字符串。所以我所做的只是使用一个字符数组,但我被迫设置字符串的最大大小。这工作正常,但我想知道是否有任何存储结构与文件中的char *(我在某些时候malloc),然后检索它。我可以保存字符串和结构分开,然后检索它们,但它是相当混乱。如果我可以将整个结构(上面的结构)加载并保存到文件中,那将是更好的选择。谢谢!

char数组的代码如下:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>

struct d_object {
    int flags;
    int time;
    int offset;
    char filename[255];
};

int main(int argc, char **argv) {

    struct d_object fcb;

    fcb.flags=5;
    fcb.time=100000;
    fcb.offset=220;
    strncpy(fcb.filename,"myfile",255);


    int fd=open("testfile",O_RDWR);
    write(fd,&fcb,sizeof(fcb));
    close(fd);


    int fd2 = open("testfile",O_RDONLY);
    struct d_object new_fcb; 
    read(fd2,&new_fcb,sizeof(new_fcb));

    printf("read from file testfile: %s\n",new_fcb.filename);

    return 0;

}

P.S。:我没有使用STREAM函数,因为这实际上是在没有它们的嵌入式操作系统上运行。我刚刚调整了* BSD / Linux的代码,所以在提问时更有意义。

5 个答案:

答案 0 :(得分:7)

我知道可移植性不是问题,因为您正在为嵌入式系统工作。在其他情况下,您应该使用类似XML的东西。

您可以将代码转换回:

struct d_object {
    int flags;
    int time;
    int offset;
    char * filename;
};

然后单独保存每个数据:

write( fd, &record.flags, sizeof( int ) );
write( fd, &record.time, sizeof( int ) );
write( fd, &record.offset, sizeof( int ) );
int filename_length = strlen( filename );
write( fd, &filename_length, sizeof( int ) );
write( fd, record.filename, filename_length );

对于阅读,你必须分开阅读每个项目,然后是文件名:

int filename_length;

read( fd, &emptyRecord.flags, sizeof( int ) );
read( fd, &emptyRecord.time, sizeof( int ) );
read( fd, &emptyRecord.offset, sizeof( int ) );

read( filename_length, sizeof( int ), 1, file );
emptyRecord.filename = (char *) malloc( sizeof( char ) * ( filename_length  +1) );
read( fd, emptyRecord.filename, filename_length );
*( emptyRecord.filename + filename_length ) = 0;

答案 1 :(得分:2)

序列化永远不会很好。如何在字符串中存储字符串的长度,并让字符串跟随文件中的结构?这样的事情(警告,脑编译代码):

void write_object(struct d_object *s, int fd) {
    struct d_object copy = *s;
    copy.filename = (char*)strlen(s->filename);
    write(fd, &copy, sizeof(copy));
    write(fd, s->filename, (size_t)copy.filename);
}

void read_object(struct d_object *s, int fd) {
    read(fd, s, sizeof(struct d_object));
    char *filename = malloc(((size_t)s->filename) + 1);
    read(fd, filename, (size_t)s->filename);
    filename[(size_t)s->filename] = '\0';
    s->filename = filename;
}

答案 2 :(得分:2)

这里的问题实际上是一个更大问题的症状:你不应该在内存和文件之间读/写二进制数据结构。不仅没有明确的方法来读取/写入带有指向其他数据的结构(这不仅适用于字符串,还适用于嵌套数据,链接列表等),但磁盘上数据的格式取决于您的主机和C实现,不能移植到其他环境。

相反,您应该为磁盘上的数据设计一种格式,并编写函数来保存和加载数据,或使用现有格式并查找库代码以便使用它。这通常被称为“序列化”。

如果您的数据是分层的,则JSON,XML或EBML可能是合适的。如果它相当简单,平面文本文件或自制二进制格式(逐字节写入,因此它是可移植的)可能是合适的。

由于您似乎不熟悉这些问题,因此在尝试为您设计一些内容之前,编写一些代码来加载/保存一些简单的二进制文件格式(如.tga或.wav)作为练习可能是值得的。自己的数据。

答案 3 :(得分:2)

现在我知道你的问题的本质,为什么不尝试灵活的数组呢?不要使用char *filename;,而是使用char filename[1]malloc(sizeof struct d_object + filename_len)来分配您的结构。添加一个size成员,只需调用write就可以轻松地将对象写入磁盘,并通过2次调用从磁盘加载它(首先读取size元素,然后在分配后读取整个对象)

请注意,在C99中执行灵活数组的“官方”方式是[]而不是[1],但[1]可以保证在标准和其他要求的后果下工作也适用于C89。 [1]会浪费几个字节,所以如果您的编译器支持[],您可能想要使用它。

答案 4 :(得分:1)

不,没有直接在语言中构建魔法来为你做这件事。

您需要做的是编写几个函数将数据转换为可写形式,并从文件中读回。这种语言被称为“序列化”/“反序列化”,而这些语言对此更加轻松。

你的特定结构,你可以做一些事情,比如直接从结构中将二进制文件写入文件,然后用字符缓冲区的内容跟进。如果您在字符数据之前加上指定其长度的int,则可以让自己更容易上手。

当你重读这些东西时,你会想要malloc / calloc自己一块内存来保存char数据;如果您存储了大小,您将知道制作该malloc的大小。将二进制数据读入struct,将char数据读入malloc内存,将指向malloc chunk的指针存储到struct中,然后重新创建原始数据。

没有魔力。只是数据和一点肘部油脂。

修改

当我写这篇文章时,托马斯编写了一个例子。我认为我们的答案很好地相互补充;他们应该一起告诉你需要知道的一切。