模拟实现通讯录<三> (文件流)
2016-05-17 20:12
465 查看
# 运用文件操作函数实现通讯录
/*文件流实现通讯录说明: 我们平时测试代码输入数据,但是每次一旦结束程序,数据也就没有了, 比如测试通讯
录, 输入很多信息后发现有个地方需要修改,就要结束程序,下一次继续重新输入,很麻烦对不对, 文件流实现的
好处就是可以每次将我们输入的数据保存到一个文件中, 下一次直接从文件中读取,省了不少麻烦!
*/
@ 模拟实现通讯录<一> (静态实现)博客链接:/article/8496972.html
@ 模拟实现通讯录<二> (动态实现)博客链接:/article/8496976.html
<一> 代码中用到的文件操作函数(只对函数功能简介,函数参数部分详情请自己了解);
@ fopen函数简介:头文件<stdlib.h>;
函数原型:FILE * fopen(const char * path,const char * mode);
<1> path 是所要打开的文件名, mode 是以何种形式打开(mode有多种形式);
<2> 打开文件后要有与之对应的 fclose(const char *path)函数关闭文件;
返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在
errno中。
一般而言,打开文件后会做一些文件读取或写入的动作,若打开文件失败,接下来的读写动作也无法顺利进行,所以
一般在fopen()后作错误判断及处理。
@ perror函数简介:[b]头文件<stdlib.h>;[/b]
perror(s) 用来将上一个函数发生错误的原因输出到标准设备(stderr)。参数 s 所指的字符串会先打印出,后面再
加上错误原因字符串。此错误原因依照全局变量errno(这里的说法不准确,errno是一个宏,该宏返回左值) 的值来决定要
输出的字符串。
在库函数中有个errno变量,每个errno值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已
经重新设置了errno的值。perror函数只是将你输入的一些信息和现在的errno所对应的错误一起输出。
@ fwrite函数简介:[b]头文件<stdlib.h>;[/b]
函数原型: size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
函数功能:fwrite是C语言函数,指向文件写入一个数据块。
注意:这个函数以二进制形式对文件进行操作,不局限于文本文件
返回值:返回实际写入的数据块数目
(1)buffer:是一个指针,对fwrite来说,是要获取数据的地址;
(2)size:要写入内容的单字节数;
(3)count:要进行写入size字节的数据项的个数;
(4)stream:目标文件指针;
(5)返回实际写入的数据项个数count。
@ fread 函数简介:[b]头文件<stdlib.h>;[/b]
函数原型:size_t fread ( void *buffer, size_tsize,
size_tcount, FILE *stream) ;
函数功能:
fread是一个函数。从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读
取到的元素个数,如果不成功或读到文件末尾返回 0。
参数:
buffer
用于接收数据的内存地址
size
要读的每个数据项的字节数,单位是字节
count
要读count个数据项,每个数据项size个字节.
stream
输入流
fread返回值
实际读取的元素个数。如果返回值与count不相同,则可能文件结尾或发生错误。从ferror和feof获取错误信息或检测是否到达文件结尾。
文件的创建位置可以是在以下相对路径创建,也可以在任意位置创建,但是调用文件名时就必须是绝对路径,同时注
意 ‘\’ 需要进行转义; 例如:C:\Intel\Logs 就必须写为 C:\\Intel\\Logs;
<二> 代码实现
# 自定义头文件 “address.h”部分
#pragma warning(disable:4996)//消除scanf的警告; #ifndef __ADDRESS_H__//防止重定义; #define __ADDRESS_H__ #include<stdio.h> #include<stdlib.h> #include<string.h> #define NAME_MAX 20 #define SEX_MAX 4 #define HOME_MAX 10 #define TEL_MAX 13 #define MEMBER_MAX 2//刚开始电话本的容量 //姓名、性别、年龄、电话、住址 typedef struct person { char name[NAME_MAX ]; char sex[SEX_MAX]; int age; char tel[TEL_MAX ]; char home[HOME_MAX ]; }person; //通讯录成员结构体; typedef struct count { person *people; int size;//确定通讯录当前人数; int capacity ; //电话本容量; }count,*con;//指针用来在函数内部接受结构体指针; enum op { EXIT, ADD, DEL, SEARCH, MOD, SHOW, SORT, EMPTY }; void Exit(con Con);//退出函数; void Add(con Con);//新增成员; void Del(con Con);//删除成员; void Search(con Con);//查找成员; void Mod(con Con);//修改成员; void Show(con Con);//显示所有成员; void Empty(con Con);//清空通讯录; void Sort(con Con);//排序所有成员; void Check(con Con);//选择函数; void _Print(); void SaveData (con Con);//存入数据保存到文件流; void DownLoad(con Con);//从文件流中读取已经保存的数据; //void (*str[8])(con Con) = {Exit,Add,Del,Search,Mod,Show,Sort,Empty}; //函数指针数组,比switch语句方便很多! #endif //__ADDRESS_H__
# 主函数main 部分
#include"address.h" int main() { count Con = {0}; Con.size = 0;//size一定要初始化为 0; Con.capacity = MEMBER_MAX ; Con.people = (person *)malloc(MEMBER_MAX *sizeof(person)); /* 注意与malloc对应的free(); 本题直接在推出功能中进行free; */ if(Con.people == NULL)//是否开辟成功的判断; printf("out of memory\n"); else { DownLoad(&Con);//从文件读取数据; while(1) { Check(&Con); } } system("pause"); return 0; }
# 函数功能实现部分
#include"address.h" //姓名、性别、年龄、电话、住址 void _Print() { printf("*************************\n"); printf("1. 添加联系人信息\n"); printf("2. 删除指定联系人信息\n"); printf("3. 查找指定联系人信息\n"); printf("4. 修改指定联系人信息\n"); printf("5. 显示所有联系人信息\n"); printf("6. 排序所有联系人信息\n"); printf("7. 清空所有联系人\n"); printf("0. 退出\n"); printf("*************************\n"); printf("请选择功能 (0-7):>\n"); } /* 因为好几个函数都会用到查找函数,所以单独封住一个find函数; */ void Judge_Full(con Con) { if(Con->size == Con->capacity ) { /* 当前人数已经达到最大通讯录容量,则进行扩容,用realloc函数; */ person *tmp = (person *)realloc (Con->people,(Con->capacity +3)*sizeof(person)); if(tmp == NULL) printf("out of memory\n"); else Con->people = tmp; (Con->capacity) += 3; }//判断通讯录是否溢出; } int find(con Con, char arr[])//公用查找函数; { int i =0; for(;i<Con->size;i++) { if(strcmp(Con->people [i].name,arr) == 0) return i; } return -1; } void Add(con Con)//增加成员; { if(Con->size == Con->capacity ) { /* 当前人数已经达到最大通讯录容量,则进行扩容,用realloc函数; */ person *tmp = (person *)realloc (Con->people,(Con->capacity +3)*sizeof(person)); if(tmp == NULL) printf("out of memory\n"); else Con->people = tmp; (Con->capacity) += 3; }//判断通讯录是否溢出; printf("准备添加一个新成员:>\n"); printf("请输入姓名:\n"); scanf("%s",&(Con->people[Con->size].name )); printf("请输入性别:\n"); scanf("%s",&(Con->people[Con->size].sex )); printf("请输入年龄:\n"); scanf("%d",&(Con->people[Con->size].age)); printf("请输入电话:\n"); scanf("%s",&(Con->people[Con->size].tel)); printf("请输入地址:\n"); scanf("%s",&(Con->people[Con->size].home)); printf("保存成功!\n"); Con->size++;//注意:每增加一个成员,通讯录总人数size都得+1; } void Del(con Con)//删除成员; { int ret = 0; char name[20] = {0}; if(Con->size == 0) { printf("亲!当前通讯录是空的!\n"); return ; } printf("请输入需要删除人的姓名:\n"); scanf("%s",&name); ret = find(Con,name); /* 删除的方法是将最后一个成员放到删除的这个成员位置上; 注意: 不能忘记size--; */ if(ret>=0) { Con->people[ret] = Con->people[Con->size-1]; (Con->size)--; printf("删除成功!\n"); } else printf("没有找到要删除的对象!\n"); } void Search(con Con)//查找成员;直接调用find函数; { int ret = 0; char name[20] = {0}; printf("请输入要查询人的姓名:\n"); scanf("%s",&name); ret = find(Con,name); if(ret>=0) { printf("%10s\t%4s\t%3s\t%13s\t%10s\n","name","sex","age","tel","address"); printf("%10s\t%4s\t%3d\t%13s\t%10s\n",Con->people [ret].name , Con->people [ret].sex ,Con->people [ret].age, Con->people [ret].tel ,Con->people [ret].home ); } else printf("没找到该通讯录好友!\n"); } void Mod(con Con)//改变成员; { int i = 0; int ret = 0; char name[20] = {0}; char *ptr[] = {"name","sex","age","tel","address"}; //每次改变成员的单个内容; printf("请输入需要修改的联系人:\n"); scanf("%s",&name); ret = find(Con,name);//调用查找函数; if(ret>=0) { int ages = 0; char mod[20] = {0}; int sel = 0; printf("请输入需要修改的选项:\n"); printf("1.name\n2.sex\n3.age\n4.tel\n5.adress\n"); scanf("%d",&sel); printf("请输入\n"); //这块选用switch语句;比较容易看懂;其实可以进行封装; switch(sel) { case 1: scanf("%s",&mod); strcpy(Con->people [ret].name,mod); printf("修改成功!\n"); break; case 2: scanf("%s",&mod); strcpy(Con->people [ret].sex ,mod); printf("修改成功!\n"); break; case 3: scanf("%d",&ages); Con->people [ret].age = ages; printf("修改成功!\n"); break; case 4: scanf("%s",&mod); strcpy(Con->people [ret].tel ,mod); printf("修改成功!\n"); break; case 5: scanf("%s",&mod); strcpy(Con->people [ret].home ,mod); printf("修改成功!\n"); break; default: printf("修改失败!\n"); break; } } else printf("没有该成员!\n"); } void Show(con Con)//显示所有成员; { int i = 0; printf("%10s\t%4s\t%3s\t%13s\t%10s\n","name","sex","age","tel","address"); //循环打印每个通讯录成员; for(; i < Con->size ; i++) { printf("%10s\t%4s\t%3d\t%13s\t%10s\n",Con->people [i].name , Con->people [i].sex ,Con->people [i].age, Con->people [i].tel ,Con->people [i].home ); } } void Empty(con Con)//清空联系人; { /* 清空通讯录只需要将通讯录总成员数size改为 0; */ Con->size = 0; printf("通讯录已空!\n"); } void Sort(con Con)//排序所有联系人; { /* 冒泡排序所有成员的名字; */ int flag = 0; int i = 0; int j = 0; for(i = 0; i < Con->size-1 ;i++) { flag = 1; for(j = 0;j <(Con->size) - i -1;j++) { if((strcmp(Con->people[j].name , Con->people [j+1].name))> 0) { person tmp = Con->people[j]; Con->people[j] = Con->people[j+1]; Con->people[j+1] = tmp; flag = 0; } } if(flag==1) break; } } void Exit(con Con)//退出函数; { SaveData(Con); free(Con->people ); Con->people = NULL; exit(0); } //定义一个函数指针数组,方便对函数的调用! void (*str[8])(con Con) = {Exit,Add,Del,Search,Mod,Show,Sort,Empty}; void Check(con Con)//选择函数; { int select = '0'; //if(Con->size == Con->capacity ) //{ // person *tmp = (person *)realloc (Con->people ,Con->capacity +3); // if(tmp == NULL) // printf("out of memory\n"); // else // Con->people = tmp; // Con->capacity += 3; //} _Print(); scanf("%d",&select); if(select==0 || select==1|| select==2|| select==3|| select==4|| select==5|| select==6 || select==7) str[select](Con); /* 如果在main函数中不进行while循环直接在Check函数中调用自己; 缺陷是:反复递归调用Check函数,会导致栈溢出; Check(Con); */ else { printf("输入格式错误!\n"); } } void SaveData(con Con)//保存数据到文件流 { /* SaveData 的调用放在退出程序之前; */ int i = 0; FILE *pfwrite = fopen("message.txt","w"); if(pfwrite == NULL) { perror("open file for write"); exit(EXIT_FAILURE); } for(i = 0; i < Con->size ; i++) { fwrite(&(Con->people[i]), sizeof(person), 1, pfwrite); } fclose(pfwrite); } void DownLoad(con Con)//从文件里读取数据 {//DownLoad 的调用在刚创建好Con之后; main函数部分; /* 这里临时创建一个tmp的目的在于,防止因为没有增容而导致的溢出; 所以我们临时创建一个tmp,将从文件中读取的内容放在tmp中,然后判断当前 是否需要增容; 之后再将tmp的内容放入我们的数据块中; */ int i = 0; person tmp = {0}; FILE *pfread = fopen("message.txt","r"); if(pfread == NULL) { perror("open file for read"); exit(EXIT_FAILURE); } while(fread(&tmp,sizeof (person), 1, pfread)) { Judge_Full(Con); Con->people [i] = tmp; i++; Con->size ++;//每读入一个数据,当前数据数目size就增加一个; } }
Ps: 不逼自己一把,怎么能知道自己有多优秀!
Thanks!
相关文章推荐
- HDOJ1013
- 利用xtrabackup工具在线建立复制环境
- 转:shell杀死指定名称的进程
- win32程序组成
- PPT必须懂的10种配色方法
- HDU 4177 Avoiding a disaster (模拟)
- LaTeX之画图语言和画图软件
- NYOJ 639-找规律【注意一下判断等比数列】
- Number plate recognition with Tensorflow
- 小班讲课之动态规划基础背包问题
- 随笔1:shell递归遍历文件夹
- SQL中小技巧代码
- 大型网站架构系列:负载均衡详解(3)
- 共同的森林实验(类变量与实例变量)
- metasploit魔鬼训练营第一章笔记
- [其他]Gson的简单使用(开始到结束)
- 自己整理的安卓逆向学习路线图
- HDU 5676 贪心
- AFNetWorking实现GET、POST
- 抢购倒计时demo