C – 将指针数据保存/加载到文件

如果以前曾经问过这个问题,或者有一个我看不到的明显的解决方案,首先道歉。 我发现了一个类似的问题,但我相信我所要求的比前面提到的更进一步。

我的结构如下:

typedef struct { int id; char *title; char *body; } journal_entry; 

问:如何在不使用固定长度的情况下用C(而不是C ++)编写和加载指向内存的指针内容?

我错误地认为通过将titlebody写入文件我会最终得到垃圾数据而不是实际存储的信息? 我不知道日记帐分录的titlebody的大小,并且从入口到入口的大小可能会有很大差异。

我自己的阅读建议我需要取消引用指针并分别fwrite结构的每个部分。 但我不确定如何跟踪数据和结构,而不会让事情变得混乱,特别是对于较大的文件。 此外,如果这些不是我打算存储在文件中的唯一项目(例如,我可能希望稍后包含小图像,我不确定如何为了方便而订购文件结构。

另一个(可能是感知的)问题是我在加载数据时使用malloc为body / entry的字符串分配内存当我希望再次加载条目时,我怎么知道为字符串分配多少内存? 我是否需要扩展我的结构以包含int body_lenint title_len

非常感激地收到指导或建议。

(我关注Linux的观点,但它可以适应其他系统)

序列化

你想要实现的通常被称为序列化 (引用维基百科) – 或编组:

序列化是将数据结构或对象状态转换为可以在相同或另一台计算机中存储和重建的格式的过程。

指针I / O.

原则上可以读取和写入指针,例如fprintf(3)和fscanf(3)的%p转换规范(并且您可以直接writeread指针,这在机器级别就像某个intptr_t整数。但是,当由不同的过程再次读取时(例如,由于ASLR ),给定地址(例如, 0x1234F580 ……)可能是无效的或具有不同的含义。

汇总数据的序列化

您可以使用一些文本格式,如JSON (我实际上建议这样做)或其他格式,如YAML (或者可能发明自己的格式,例如受s-exprs启发)。 优先考虑文本格式(并且自1980年以来Unix有这种习惯)到二进制格式(如XDR , ASN / 1 ,……)是一种习惯。 许多协议(HTTP,SMTP,FTP, JSONRPC ….)是文本协议

请注意,在当前系统上,I / O比计算慢得多,因此文本编码和解码的相对成本与网络或磁盘IO相比很小(请参阅此处的答案表)

某些聚合数据(例如C中的struct )的编码通常是组合的,通过组合基本标量数据(数字,字符串,……)的编码,您可以编码某些更高级别的数据类型。

序列化库

大多数格式(尤其是JSON)都有几个免费软件库来编码/解码它们,例如Jansson , JsonCPP等。

建议:

使用JSON并将您的journal_entry格式化为类似的JSON对象

 { "id": 1234, "title": "Some Title Here", "body": "Some body string goes here" } 

具体来说,您将使用一些JSON库并首先将您的journal_entry转换为某种JSON类型(反之亦然),然后使用该库对JSON进行编码/解码

数据库

你也可以考虑数据库方法(例如sqlite等……)


PS。 闭包的序列化(或包含指向代码的指针的任何内容)可能具有挑战性。 您需要确定具体含义。

PPS。 某些语言为序列化和编组提供内置支持。 例如,Ocaml有一个Marshal模块,Python有泡菜

你把这个结构存储在内存中是不对的,因为一旦指针所指向的字符串消失了,就无法检索它们。 从实际的角度来看,一种方法是声明有限长度的字符串(如果你知道你的字符串有一个长度限制):

 typedef struct { int id; char title[MAX_TITLE_LEGNTH]; char body[MAX_BODY_LENGTH]; } journal_entry; 

如果需要使用malloc分配标题和正文,则可以使用“header”元素来存储整个结构的长度。 将结构写入文件时,可以使用此元素确定需要读取的字节数。

即写:

 FILE* fp = fopen(,"wb"); size_t size = sizeof(id)+strlen(title)+1+strlen(body)+1; fwrite(&size, sizeof(size), 1, fp); fwrite(&id, sizeof(id), 1, fp); fwrite(title, sizeof(char), strlen(title)+1, fp); fwrite(body, sizeof(char), strlen(body)+1, fp); fclose(fp); 

阅读(不是特别安全的实施,只是为了提出想法):

 FILE* fp = fopen(,"rb"); size_t size; int read_bytes = 0; struct journal_entry je; fread(&size, sizeof(size), 1, fp); void* buf = malloc(size); fread(buf, size, 1, fp); fclose(fp); je.id = *((int*)buf); // might break if you wrote your file on OS with different endingness read_bytes += sizeof(je.id) je.title = (char*)(buf+read_bytes); read_bytes += strlen(je.title)+1; je.body = (char*)(buf+read_bytes); // other way would be to malloc je.title and je.body and destroy the buf 

在内存中,您可以将字符串存储为数组的指针。 但是在磁盘上的文件中,您通常会直接存储数据。 一种简单的方法是存储包含大小的uint32_t ,然后存储字符串的实际字节。 您还可以在文件中存储以null结尾的字符串,并在读取时只扫描空终止符。 第一种方法使得在读取时更容易预先分配所需的缓冲区空间,而无需两次传递数据。