目录
思路
代码实现
完整代码(可自取)
思路
在前面的文章中,已经讲解了动态版本的通讯录的实现,但是动态通讯录存在一个致命缺陷,就是它不能自动保存数据,而前面一篇文章中学到了数据持久化的方法之一:即把数据存放在磁盘文件上,便可以实现数据持久化。
具体应该如何做呢?
假如我们在退出的时候,通过文件操作,把我们所写的数据存在磁盘文件里,然后我们再进行下一次的使用的时候,在初始化阶段就从磁盘中读取这些数据,这不就实现了。
代码实现
代码的实现并不困难,只不过是在动态内存版本的基础上进行了一些文件操作,用来保存和读取数据。
从文件中读取信息(初始化阶段完成)
//读通讯录文件信息 //size_t fread(void* ptr, size_t size, size_t count, FILE* stream) void Load_Contact(struct contact* p) { //二进制读 FILE* pfR = fopen("通讯录.txt", "rb"); if (pfR == NULL) { //读取失败打印错误报告 perror("Load_Contact::fopen"); return; } struct message pf = { 0 }; //fread返回值为读取的完整的元素个数,这里读取成功返回1,失败0 while (fread(&pf, sizeof(pf), 1, pfR)) { //判断是否增容 check_capacity(p); p->data[p->sz] = pf; p->sz++; } //关闭文件 fclose(pfR); pfR = NULL; } //初始化通讯录 void Init_contact(struct contact* p) { assert(p); //开辟空间 p->data =(struct message*) malloc(DEFAULT_SZ * sizeof(struct message)); //假如开辟失败,报错 if (p->data == NULL) { printf("%s\n", strerror(errno)); return; } p->sz = 0; p->capacity = DEFAULT_SZ; //加载通讯录信息 Load_Contact(p); }
这里在动态版本的基础上,在初始化阶段加入了一个Load_Contact()函数,这个是用来以二进制读的方式打开文件,并且把读取到的信息放在结构体pf里,然后再将pf赋值到p指向的data数组的下标为size空间。
将数据写入文件(退出时保存信息)
这一步是为了将我们本次所写的数据,写入到文件中去,以备下一次打开时好从中读取数据。具体代码如下:
//写数据(保存通讯录信息) //size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream) void Storage_Contact(struct contact* p) { //二进制形式写 FILE* pfW = fopen("通讯录.txt", "wb"); if (pfW == NULL) { //失败则打印错误报告 perror("Storage_Contact::fopen"); return; } int i = 0; //将sz个数据都写入到文件中 for (i = 0; i < p->sz; i++) { fwrite(p->data + i, sizeof(struct message), 1, pfW); } //关闭 fclose(pfW); pfW = NULL; }
正是有了这一步,我们在退出时会创建一个文件来保存信息。供下一次读取。如以下视频所示:在我第二次运行程序时,上一次的数据已经加载完毕了。实现了数据持久化。
完整代码(可自取)
.h头文件
#pragma once #include<stdio.h> #include<string.h> #include<assert.h> #include<stdlib.h> #include<Windows.h> #include<errno.h> #define NAME 20 #define SEX 5 #define TELE 12 #define ADDR 30 #define DEFAULT_SZ 3//初始容量 #define INC_SZ 2//扩容 //联系人信息 struct message { //姓名 char name[NAME]; //性别 char sex[SEX]; //电话 char tele[TELE]; //住址 char addr[ADDR]; //年龄 int age; }; //通讯录 struct contact { struct message* data; int sz;//个数 int capacity;//通讯录容量 }; //初始化通讯录 void Init_contact(struct contact* p); //动态增加联系人 void Add_contact(struct contact* p); //显示联系人 void Show_contact(const struct contact* p); //删除联系人 void Dele_contact(struct contact* p); //修改联系人信息 void revise_contact(struct contact* p); //查找联系人信息 void Find_contact(const struct contact* p); //排序联系人 void Sort_contact(struct contact* p); //清空联系人 void Clean(struct contact* p); //释放空间 void Destory_contact(struct contact* p); //保存数据 void Storage_Contact(struct contact* p);
.c源文件(函数定义)
#include"Contact_exe.h" //是否判断增容 int check_capacity(struct contact* p) { //当联系人个数 == 通讯录容量时,增容INC_SZ个内存空间 if (p->sz == p->capacity) { struct message* ptr = (struct message*)realloc(p->data, (p->capacity + INC_SZ) * sizeof(struct message)); if (ptr == NULL)//判断是否增容失败 { printf("%s\n", strerror(errno)); return 0; } else { p->data = ptr;//增容成功,data就指向这块新开辟的空间 p->capacity += INC_SZ;//容量+=INC_SZ //printf("增容成功!\n"); return 1; } } //不需要增容 else return 1; } //读通讯录文件信息 //size_t fread(void* ptr, size_t size, size_t count, FILE* stream) void Load_Contact(struct contact* p) { //二进制读 FILE* pfR = fopen("通讯录.txt", "rb"); if (pfR == NULL) { //读取失败打印错误报告 perror("Load_Contact::fopen"); return; } struct message pf = { 0 }; //fread返回值为读取的完整的元素个数,这里读取成功返回1,失败0 while (fread(&pf, sizeof(pf), 1, pfR)) { //判断是否增容 check_capacity(p); p->data[p->sz] = pf; p->sz++; } //关闭文件 fclose(pfR); pfR = NULL; } //初始化通讯录 void Init_contact(struct contact* p) { assert(p); //开辟空间 p->data =(struct message*) malloc(DEFAULT_SZ * sizeof(struct message)); //假如开辟失败,报错 if (p->data == NULL) { printf("%s\n", strerror(errno)); return; } p->sz = 0; p->capacity = DEFAULT_SZ; //加载通讯录信息 Load_Contact(p); } //增加联系人 void Add_contact(struct contact* p) { assert(p); if (0 == check_capacity(p)) { printf("%s\n", strerror(errno)); return; } printf("请输入姓名:->"); scanf("%s", p->data[p->sz].name); printf("请输入性别:->"); scanf("%s", p->data[p->sz].sex); printf("请输入电话:->"); scanf("%s", p->data[p->sz].tele); printf("请输入住址:->"); scanf("%s", p->data[p->sz].addr); printf("请输入年龄:->"); scanf("%d", &(p->data[p->sz].age)); system("cls"); printf("增加成功!\n"); printf("\n"); p->sz++; } //显示联系人 void Show_contact(const struct contact* p) { assert(p); int i = 0; printf("%-20s\t%-5s\t%-12s\t%-20s\t%-5s\n", "姓名", "性别", "电话", "住址", "年龄"); for (i = 0; i < p->sz; i++) { printf("%-20s\t%-5s\t%-12s\t%-20s\t%-5d\n", p->data[i].name, p->data[i].sex, p->data[i].tele, p->data[i].addr, p->data[i].age); } } int find_name(const struct contact* p, char arr[]) { assert(p); int i = 0; for (i = 0; i < p->sz; i++) { if (0 == strcmp(p->data[i].name, arr)) return i; } return -1; } //删除联系人 void Dele_contact(struct contact* p) { assert(p); char del_name[NAME]; printf("请输入要删除联系人的姓名:->"); scanf("%s", del_name); //查找该联系人 int ret = find_name(p, del_name); if (ret == -1) printf("查无此人!\n"); else { int j = 0; for (j = ret; j < p->sz - 1; j++) { p->data[j] = p->data[j + 1]; } p->sz--; system("cls"); printf("删除成功!\n"); printf("\n"); } } //修改菜单栏 void menu_() { printf("***********************************\n"); printf("****** 1、修改联系人姓名 ******\n"); printf("****** 2、修改联系人电话 ******\n"); printf("****** 3、修改联系人年龄 ******\n"); printf("****** 4、修改联系人住址 ******\n"); printf("****** 5、修改联系人性别 ******\n"); printf("****** 0、返回主菜单 ******\n"); } //修改联系人信息 void revise_contact(struct contact* p) { assert(p); char del_name[NAME]; printf("请输入要修改信息的联系人的姓名:->"); scanf("%s", del_name); int ret = find_name(p, del_name); if (ret == -1) { printf("查无此人!\n"); printf("\n"); } else { int in_put = 0; do { menu_(); scanf("%d", &in_put); switch (in_put) { case 1: printf("请输入修改后的姓名:->"); scanf("%s", p->data[ret].name); system("cls"); printf("姓名修改成功!\n"); break; case 2: printf("请输入修改后的电话:->"); scanf("%s", p->data[ret].tele); system("cls"); printf("电话修改成功!\n"); break; case 3: printf("请输入修改后的年龄:->"); scanf("%d", &(p->data[ret].age)); system("cls"); printf("年龄修改成功!\n"); break; case 4: printf("请输入修改后的住址:->"); scanf("%s", p->data[ret].addr); system("cls"); printf("住址修改成功!\n"); break; case 5: printf("请输入修改后的性别:->"); scanf("%s", p->data[ret].sex); system("cls"); printf("性别修改成功!\n"); break; case 0: printf("取消修改!\n"); break; default: printf("输入错误!\n"); break; } } while (in_put); } } //查找联系人信息 void Find_contact(const struct contact* p) { assert(p); char del_name[NAME]; printf("请输入要查找联系人的姓名:->"); scanf("%s", del_name); system("cls"); //查找该联系人 int ret = find_name(p, del_name); if (ret == -1) printf("查无此人!\n"); else { printf("%-20s\t%-5s\t%-12s\t%-20s\t%-5s\n", "姓名", "性别", "电话", "住址", "年龄"); printf("%-20s\t%-5s\t%-12s\t%-20s\t%-5d\n", p->data[ret].name, p->data[ret].sex, p->data[ret].tele, p->data[ret].addr, p->data[ret].age); } } //排序菜单 void menu_sort() { printf("****** 1、姓名 ******\n"); printf("****** 2、住址 ******\n"); printf("****** 3、年龄 ******\n"); printf("****** 4、性别 ******\n"); printf("****** 0、退出 ******\n"); } //姓名排序 int cmp_name(const void* e1, const void* e2) { return strcmp(((struct message*)e1)->name, ((struct message*)e2)->name); } //住址排序 int cmp_addr(const void* e1, const void* e2) { return strcmp(((struct message*)e1)->addr, ((struct message*)e2)->addr); } //年龄排序 int cmp_age(const void* e1, const void* e2) { return ((struct message*)e1)->age - ((struct message*)e2)->age; } //性别排序 int cmp_sex(const void* e1, const void* e2) { return strcmp(((struct message*)e1)->sex, ((struct message*)e2)->sex); } //排序联系人 void Sort_contact(struct contact* p) { int s = 0; do { //排序菜单 menu_sort(); printf("请选择排序类型:->"); scanf("%d", &s); system("cls"); switch (s) { case 1: qsort(p->data, p->sz, sizeof(struct message), cmp_name); printf("排序成功!\n"); break; case 2: qsort(p->data, p->sz, sizeof(struct message), cmp_addr); printf("排序成功!\n"); break; case 3: qsort(p->data, p->sz, sizeof(struct message), cmp_age); printf("排序成功!\n"); break; case 4: qsort(p->data, p->sz, sizeof(struct message), cmp_sex); printf("排序成功!\n"); break; case 0: printf("退出排序\n"); break; default: printf("输入有误!\n"); break; } } while (s); } //清空联系人 void Clean(struct contact* p) { p->sz = 0; printf("清空成功!\n"); } //释放空间 void Destory_contact(struct contact* p) { free(p->data); p->data=NULL; p->sz = 0; p->capacity = 0; } //写数据(保存通讯录信息) //size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream) void Storage_Contact(struct contact* p) { //二进制形式写 FILE* pfW = fopen("通讯录.txt", "wb"); if (pfW == NULL) { //失败则打印错误报告 perror("Storage_Contact::fopen"); return; } int i = 0; for (i = 0; i < p->sz; i++) { fwrite(p->data + i, sizeof(struct message), 1, pfW); } fclose(pfW); pfW = NULL; }
.c源文件(测试)
#include"Contact_exe.h" void menu() { printf("--------------------------------------------------------------\n"); printf("--------- 1、增加联系人 2、删除指定联系人 ---------\n"); printf("--------- 3、修改联系人信息 4、查找联系人 ---------\n"); printf("--------- 5、排序联系人 6、显示已有联系人 ---------\n"); printf("--------- 0、退出并保存信息 7、清空联系人 ---------\n"); printf("--------------------------------------------------------------\n"); } int main() { int input = 0; //创建通讯录 struct contact con; //初始化通讯录(读之前通讯录的信息) Init_contact(&con); do { menu(); printf("请选择:->"); scanf("%d", &input); system("cls"); switch (input) { case 1: //增加联系人 Add_contact(&con); break; case 2: //删除联系人 Show_contact(&con); Dele_contact(&con); break; case 3: //修改联系人信息 Show_contact(&con); revise_contact(&con); break; case 4: //查找联系人信息 Find_contact(&con); break; case 5: //排序联系人信息 Show_contact(&con); Sort_contact(&con); break; case 6: //显示联系人 system("cls"); Show_contact(&con); printf("\n"); break; case 7: //清空联系人 Clean(&con); break; case 0: //保存数据 Storage_Contact(&con); //释放空间 Destory_contact(&con); break; default: printf("输入错误!\n"); break; } } while (input); return 0; }