一、说明
这次的通讯录为文件保存的版本,
需求说明:
1、增加保存功能,可以存放到文件中。
2、增加读取功能,读取之前保存的通讯录文件。
小提示:
今天写代码遇到一个BUG,无论怎么更改代码都报错,
后来才调试出:如果你要在A函数中调用B函数,即使在头文件中声明了,但是在函数文件中,一定要先定义B。因为程序是从上至下一行一行读取的,编译不会通过,但是报的错会很奇怪,如报scanf的错,这样很难通过编译器找到错因。所以写函数一定要分先后。
二、更改代码段
首先是保存功能:
void SaveContact(struct Contact* ps)
{
FILE* pfWrite;
pfWrite = fopen("C:\\Users\\asus\\Desktop\\contact.dat", "wb");
if (pfWrite == NULL)
{
printf("%s\n", strerror(errno));
return;
}
//打开成功
//写通讯录中的数据到文件中
int i = 0;
for (i = 0; i < ps->size; i++)
{
fwrite(&(ps->data[i]), sizeof(PeoInfo), 1, pfWrite);
}
//关闭文件
fclose(pfWrite);
pfWrite = NULL;
}
这个函数创建了一个文件指针,指向要保存的数据的地址文件。
然后进行指针判空操作,
如果指针存在,则进行一个for循环,
将当前存放的通讯录中的数据一条一条地通过fwrite函数写进文件里。
fwrite函数:写文件函数。
4个参数,
第一个是要读取的数据元素的地址,
第二个是读取一个元素的大小,
第三个是读取元素的数量,
第四个是要写到哪个文件里去,指向那个文件的指针。
这样就完成了保存文件操作。
那么读取通讯录呢?
对于读取通讯录,我们有两个问题:
1、怎么读
2、扩容问题
首先解决第一个问题。
读文件要用到的函数是:
fread();
同样是4个参数,
不过操作不一样了。
第一个是被写的数据元素的地址,
第二个是写的一个元素的大小,
第三个是写元素的数量,
第四个是要读哪个文件,指向那个文件的指针。
综上,文件读写函数分别是:
读文件函数,fread,意思是要把文件里的内容读出来,写到变量中去;
写文件函数,fwrite,意思是要把变量中的数据写进文件里去。
这里的读写均是针对文件。
所以,我们先读这个文件里的数据,存放到临时结构体变量tmp中:
PeoInfo tmp = { 0 };
FILE* PfRead = fopen("C:\\Users\\asus\\Desktop\\contact.dat", "rb");
if (PfRead == NULL)
{
printf("LoadContact::%s\n", strerror(errno));
return;
}
//读取文件,存放到通讯录中
read(&tmp, sizeof(PeoInfo), 1, PfRead);
而fread具有返回类型,是size_t,意思是读取了几个元素;
如果没有读满一个元素,就返回0.
这里我们可以很好的运用这个返回值来解决第二个问题:扩容问题。
首先,我们定义一开始只动态开辟3个元素的内存,
那么前三个好放,我们只需要
ps->data[ps->size] = tmp;
ps->size++;
但是当放满三个后,需要扩容,我们就在前面加上一个扩容函数。
最后,用返回类型来控制while循环的次数即可:
void LoadContact(Contact* ps)
{
PeoInfo tmp = { 0 };
FILE* PfRead = fopen("C:\\Users\\asus\\Desktop\\contact.dat", "rb");
if (PfRead == NULL)
{
printf("LoadContact::%s\n", strerror(errno));
return;
}
//读取文件,存放到通讯录中
while (fread(&tmp, sizeof(PeoInfo), 1, PfRead))
{
CheckCapacity(ps);
ps->data[ps->size] = tmp;
ps->size++;
}
//关闭文件
fclose(PfRead);
PfRead = NULL;
}
而这个函数,我们可以写到初始化函数中:
void InitContact(struct Contact* ps)
{
ps->data = (struct PeoInfo*)malloc(3 * sizeof(struct PeoInfo));
if (ps->data == NULL)
{
return;
}
ps->size = 0;
ps->capacity = DEFAULT_SZ;
//把文件中已经存放的通讯录中的信息加载到通讯录中
LoadContact(ps);
}
这样一份文件版的通讯录就写完了。