读写内存数据
Reading and writing in-memory data
在下面的示例中,我采用一个在内存中占用 32 个字节的结构并将其写入文件并读回——即将数据序列化为二进制格式:
#include <stdio.h>
typedef struct _Person {
char name[20];
int age;
double weight;
} Person;
int main(void)
{
Person tom = (Person) {.name="Tom", .age=20, .weight=125.0};
// write the struct to a binary file
FILE *fout = fopen("person.b", "wb");
fwrite(&tom, sizeof tom, 1, fout);
fclose(fout);
// read the binary data and set the person to that
Person unknown;
FILE *fin = fopen("person.b", "rb");
fread(&unknown, sizeof unknown, 1, fin);
fclose(fin);
// confirm all looks ok
printf("{name=%s, age=%d, weight=%f}", unknown.name, unknown.age, unknown.weight);
}
但是请注意,这些都是堆栈上的值,不涉及 pointers/indirection。例如,当可能涉及多个指针、多个变量可能指向相同的内存位置等时,如何将数据序列化到一个文件中。这实际上是协议缓冲区的作用吗?
好的,所以你需要一个二进制文件。我很久以前就这样做过。没关系。当您移动到另一个平台或位数时,它就会中断。我正在以旧方式教学,因为它是一个很好的起点。更新的方法现在很流行,因为它们在更改平台或位数时仍然有效。
将记录写入文件时,我会使用这样的结构:
typedef struct _Person {
char name[20];
int age;
double weight;
} Person;
typedef struct _Thing {
char name[20];
};
typedef struct _Owner {
int personId;
int thingId;
} Owner;
查看 Owner
结构如何具有 Id 成员。这些只是其他结构数组的索引。
这些可以一个接一个地写到一个文件中,通常直接写一个整数作为前缀,表示每种记录有多少条。 reader 只是分配了一个 malloc
足够大的结构数组来容纳它们。当我们在内存中添加更多项目时,我们使用 realloc
调整数组的大小。我们可以(并且应该)也标记为删除(比如将名称的第一个字符设置为 0)并在以后重新使用该记录。
作者长这样:
void writeall(FILE *h, Person *allPeople, int nPeople, Thing *allThings, int nThings, Owner *allOwners, int nOwners)
{
// Error checking omitted for brevity
fwrite(&nPeople, sizeof(nPeople), 1, h);
fwrite(allPeople, sizeof(*allPeople), nPeople, h);
fwrite(&nThings, sizeof(nThings), 1, h);
fwrite(allThings, sizeof(*allThings), nThings, h);
fwrite(&nOwners, sizeof(nOwners), 1, h);
fwrite(allOwners, sizeof(*allOwners), nOwners, h);
}
reader 依次如下所示:
int writeall(FILE *h, Person **allPeople, int *nPeople, int *aPeople, Thing **allThings, int *nThings, int *aThings, Owner **allOwners, int *nOwners, int *aOwners)
{
*aPeople = 0; // Don't crash on bad read
*aThigns = 0;
*aOwners = 0;
*allPeople = NULL;
*allThings = NULL;
*allOwners = NULL;
if (1 != fread(nPeople, sizeof(*nPeople), 1, h)) return 0;
*allPeople = malloc(sizeof(**allPeople) * *nPeople);
if (!allPeople) return 0; // OOM
*aPeople = *nPeople;
if (*nPeople != fread(*allPeople, sizeof(**allPeople), nPeople, h)) return 0;
if (1 != fread(nThings, sizeof(*nThings), 1, h)) return 0;
*allThings = malloc(sizeof(**allThings) * *nThings);
if (!allThings) return 0; // OOM
*aThings = *nThings;
if (*nThings != fread(*allThings, sizeof(**allThings), nThings, h)) return 0;
if (1 != fread(nOwners, sizeof(*nOwners), 1, h)) return 0;
*allOwners = malloc(sizeof(**allOwners) * *nOwners);
if (!allOwners) return 0; // OOM
*aOwners = *nOwners;
if (*nOwners != fread(*allOwners, sizeof(**allOwners), nOwners, h)) return 0;
return 1;
}
有一种将堆区域直接写入磁盘并再次读回的旧技术。我建议永远不要使用它,也不要将指针存储在磁盘上。那就是安全噩梦。
当内存便宜时,我会谈论如何使用块分配和链接块来部分动态更新磁盘上的记录;但是现在对于你在这个级别会遇到的问题,我说不要打扰,只需将整个内容读入 RAM,然后再写回来。最终您将学习数据库,它会为您处理这些事情。
在下面的示例中,我采用一个在内存中占用 32 个字节的结构并将其写入文件并读回——即将数据序列化为二进制格式:
#include <stdio.h>
typedef struct _Person {
char name[20];
int age;
double weight;
} Person;
int main(void)
{
Person tom = (Person) {.name="Tom", .age=20, .weight=125.0};
// write the struct to a binary file
FILE *fout = fopen("person.b", "wb");
fwrite(&tom, sizeof tom, 1, fout);
fclose(fout);
// read the binary data and set the person to that
Person unknown;
FILE *fin = fopen("person.b", "rb");
fread(&unknown, sizeof unknown, 1, fin);
fclose(fin);
// confirm all looks ok
printf("{name=%s, age=%d, weight=%f}", unknown.name, unknown.age, unknown.weight);
}
但是请注意,这些都是堆栈上的值,不涉及 pointers/indirection。例如,当可能涉及多个指针、多个变量可能指向相同的内存位置等时,如何将数据序列化到一个文件中。这实际上是协议缓冲区的作用吗?
好的,所以你需要一个二进制文件。我很久以前就这样做过。没关系。当您移动到另一个平台或位数时,它就会中断。我正在以旧方式教学,因为它是一个很好的起点。更新的方法现在很流行,因为它们在更改平台或位数时仍然有效。
将记录写入文件时,我会使用这样的结构:
typedef struct _Person {
char name[20];
int age;
double weight;
} Person;
typedef struct _Thing {
char name[20];
};
typedef struct _Owner {
int personId;
int thingId;
} Owner;
查看 Owner
结构如何具有 Id 成员。这些只是其他结构数组的索引。
这些可以一个接一个地写到一个文件中,通常直接写一个整数作为前缀,表示每种记录有多少条。 reader 只是分配了一个 malloc
足够大的结构数组来容纳它们。当我们在内存中添加更多项目时,我们使用 realloc
调整数组的大小。我们可以(并且应该)也标记为删除(比如将名称的第一个字符设置为 0)并在以后重新使用该记录。
作者长这样:
void writeall(FILE *h, Person *allPeople, int nPeople, Thing *allThings, int nThings, Owner *allOwners, int nOwners)
{
// Error checking omitted for brevity
fwrite(&nPeople, sizeof(nPeople), 1, h);
fwrite(allPeople, sizeof(*allPeople), nPeople, h);
fwrite(&nThings, sizeof(nThings), 1, h);
fwrite(allThings, sizeof(*allThings), nThings, h);
fwrite(&nOwners, sizeof(nOwners), 1, h);
fwrite(allOwners, sizeof(*allOwners), nOwners, h);
}
reader 依次如下所示:
int writeall(FILE *h, Person **allPeople, int *nPeople, int *aPeople, Thing **allThings, int *nThings, int *aThings, Owner **allOwners, int *nOwners, int *aOwners)
{
*aPeople = 0; // Don't crash on bad read
*aThigns = 0;
*aOwners = 0;
*allPeople = NULL;
*allThings = NULL;
*allOwners = NULL;
if (1 != fread(nPeople, sizeof(*nPeople), 1, h)) return 0;
*allPeople = malloc(sizeof(**allPeople) * *nPeople);
if (!allPeople) return 0; // OOM
*aPeople = *nPeople;
if (*nPeople != fread(*allPeople, sizeof(**allPeople), nPeople, h)) return 0;
if (1 != fread(nThings, sizeof(*nThings), 1, h)) return 0;
*allThings = malloc(sizeof(**allThings) * *nThings);
if (!allThings) return 0; // OOM
*aThings = *nThings;
if (*nThings != fread(*allThings, sizeof(**allThings), nThings, h)) return 0;
if (1 != fread(nOwners, sizeof(*nOwners), 1, h)) return 0;
*allOwners = malloc(sizeof(**allOwners) * *nOwners);
if (!allOwners) return 0; // OOM
*aOwners = *nOwners;
if (*nOwners != fread(*allOwners, sizeof(**allOwners), nOwners, h)) return 0;
return 1;
}
有一种将堆区域直接写入磁盘并再次读回的旧技术。我建议永远不要使用它,也不要将指针存储在磁盘上。那就是安全噩梦。
当内存便宜时,我会谈论如何使用块分配和链接块来部分动态更新磁盘上的记录;但是现在对于你在这个级别会遇到的问题,我说不要打扰,只需将整个内容读入 RAM,然后再写回来。最终您将学习数据库,它会为您处理这些事情。