我正在开发一个类似数据库的应用程序,它存储一个包含以下内容的结构:
struct Dictionary
{
char *key;
char *value;
struct Dictionary *next;
};
如您所见,我使用链接列表来存储信息。但是当用户退出程序时问题就开始了。我希望将信息存储在某个地方。所以我想使用fopen将链表存储在永久或临时文件中,然后,当用户启动程序时,检索链表。以下是将链接列表打印到控制台的方法:
void PrintList()
{
int count = 0;
struct Dictionary *current;
current = head;
if (current == NULL)
{
printf("\nThe list is empty!");
return;
}
printf(" Key \t Value\n");
printf(" ======== \t ========\n");
while (current != NULL)
{
count++;
printf("%d. %s \t %s\n", count, current->key, current->value);
current = current->next;
}
}
所以我正在考虑修改此方法以通过fprintf而不是printf打印信息,然后程序将从文件中获取信息。有人可以帮助我如何读写这个文件吗?应该是什么样的文件,临时的还是常规的?我应该如何格式化文件(就像我想的那样首先是键,然后是值,然后是换行符)?
答案 0 :(得分:2)
该文件应该是常规的。下次启动应用程序时,不保证临时文件存在。此外,你的格式对于人类看起来很好,对机器来说也不是那么好。我建议您创建自己的二进制文件格式还是使用XML(或者可能是JSON?)。您可能很容易将其格式化为
key1\0value1\0key2\0value2\0....
我会写一个快速的例子是psuedoish代码:
//To write...
Dictionary *this=begin_list;
while(this!=null){
for(i=0;i<strlen(this->key);i++){
write_byte(this->key[i]);
}
for(i=0;i<strlen(this->value);i++){
write_byte(this->value[i]);
}
this=this->next;
}
//to read...
Dictionary *prev;
Dictionary *this;
char *buffer;
while(!eof){
buffer=malloc(MAX_STRING_LEN);
int i=0;
this=malloc(sizeof(Dictionary)
while(i<MAX_STRING_LEN){ //note no error checking
buffer[i]=read_byte();
if(buffer[i]==0){
break;
}
}
this->key=buffer;
buffer=malloc(MAX_STRING_LEN)
while(i<MAX_STRING_LEN){ //note no error checking
buffer[i]=read_byte();
if(buffer[i]==0){
break;
}
}
this->value=buffer;
if(prev!=null){
prev->next=this;
}
this->next=null;
prev=this;
}
我知道这是一个糟糕的例子。我认为scanf或类似的东西可能会使工作变得更容易,但我的C技能正在变得生疏。
答案 1 :(得分:2)
根本问题是指针不会转换为外部存储。无法保证当程序再次执行时,它将具有相同的内存范围(地址)。鉴于这一原则,存在存储数据的替代方法。
持久数据的处理:
1.使用小型或大型数据库
2.以可扫描格式将数据转换为ASCII文本
3.使用固定长度的二进制记录
4.使用可变大小的二进制记录
5.使用文件偏移而不是指针实现字典数据结构。
使用数据库
让专业应用程序(已经过测试和运行)管理您的数据。这使您可以专注于使用数据而不是存储和撤销。
转换为可扫描格式
这里的想法是以易于检索和维护的格式将数据写入文件。示例包括逗号分隔值(CSV),XML和INI。这需要您自己的代码来读取和写入数据。有图书馆可以提供帮助。
使用固定长度的二进制记录
使用固定长度记录,将从文件中读取数据并将其插入到字典中。就传输数据而言,二进制文件非常有效,但不是非常便携,特别是当操作系统版本发生变化,平台更改或编译器版本发生变化时。文本记录可能会浪费空间。
使用可变大小的二进制记录
该技术节省了空间,但增加了处理时间。必须处理每条记录才能找到下一条记录的位置。随机访问记录很困难。否则类似于固定长度的二进制记录。
在文件中实施字典数据结构
与基于内存的数据结构相同的算法,除了使用文件偏移而不是指针。新记录可以附加到文件末尾。回收已删除的条目很困难,并且会导致碎片化。可以通过编写新文件来解决碎片问题。如果您正在经历这么多努力,那么您也可以使用现有的数据库应用程序。
答案 2 :(得分:1)
您可以读取或写入文件的一种方法是使用freopen,如下所示: freopen(“file.out”,“wt”,stdout),那么你就是printf将转到file.out,你不需要修改代码。
您可以以纯文本格式存储信息,但我认为最好的方法是将信息保存在二进制文件中。 您可以查看有关fread和fwrite的搜索信息的更多信息。
答案 3 :(得分:0)
这是解决问题的一种方法。
为您的列表项创建数据结构,如下所示:
struct DictionaryArchive {
char key[MAX_KEY_LENGTH];
char value[MAX_VALUE_LENGTH];
int next;
};
您需要根据您期望的数据确定MAX_KEY_LENGTH
和MAX_VALUE_LENGTH
的值。
现在,将链接列表转换为这些结构的数组。您将存储下一个项目的数组索引,而不是存储用于定位下一个项目的指针。这会将您的列表转换为一种格式,其中每个元素都是可预测的大小,整个列表是一个连续的内存范围。现在,您可以fwrite
将此数组转换为二进制文件以对其进行存档,然后fread
将其还原以恢复它。
使用上面固定大小的char
数组的一种更节省空间的替代方法是改为定义自定义文件格式而不是使用静态结构。对于您的情况,您可以使用这样的文件格式以可检索的方式存储数据:
next
指向尾部的指针key_length
key_length
元素的{8}字符数组,key_data
value_length
value_length
元素的{8}字符数组,value_data
现在,您可以遍历列表,按节点将数据转储到文件节点。要重新构建数据,请阅读二进制文件,为每个条目生成新的struct Dictionary
元素,并按照它们在文件中出现的顺序将它们链接在一起。
将数据写入数据文件的代码看起来像这样(未经测试,仅用于说明目的):
FILE* fd;
size_t len;
struct Dictionary* pDict = list_head;
fd = fopen("output_file.dat", "w");
// Walk through the list, storing each node
while (pDict != NULL) {
// Store key
len = strlen(pDict->key);
fwrite(&len, sizeof(len), 1, fd);
fwrite(pDict->key, len, sizeof(char), fd);
// Store value
len = strlen(pDict->value);
fwrite(&len, sizeof(len), 1, fd);
fwrite(pDict->value, len, sizeof(char), fd);
// Move to next list node
pDict = pDict->next;
};
fclose(fd);
读取数据的代码非常相似(读取而不是写入,并为每个循环迭代创建一个新的struct Dictionary
对象。)