C语言: 文件的读写
extra:通讯录文件版实现
文件怎么操作?用什么函数?FILE*是什么?(浅1)
通讯录的进一步
通讯录静态版博客 , 通讯录动态版博客 , 源码
1.为什么使用文件?
因为我们执行代码的时候,结束运行后,数据全部销毁
例如说我们在通讯录使用时,会发现,每次输入的信息没法保留,必须重新输入
所以我们应该使用文件,保存信息,并且读取信息
2.什么是文件?
程序文件 (要运行所需要的文件)
数据文件 (程序数据内容,C语言有“.c”,java有“.java .class”)
例如java “.java”为文本文件 ,“.class”为二进制文件(看不懂无所谓,只要知道两种文件存储数据的方式不同)
3. 文件指针
3.1 FILE*就是文件指针,指向的是一个结构体,包含的一系列信息,但是这些不需要了解,编译器会解决,只要使用这个指针加上对应函数就可以使用文件了。
3.2 文件的打开
FILE * fopen ( const char * filename, const char * mode );
FILE * const char * filename const char * mode
返回值 文件名 打开方式
文件指针(只要有它就可以用文件) 当前路径下的文件(或者完全路径的文件名) 有很多,如下:
一般用到四个"w",“r”,“wb”,“rb”
带b的为二进制文件,不带就位文本文件(两种数据存储不同,使用错误会导致结果天翻地覆的不同!)
例如,1 在文本文件中存的是字符 ‘1’,也就是说存的是0x31,但是如果在二进制文件中存的是0x01 ,0x00,0x00,0x00,
它是以数据在内存中存储的形式,将需要保存的所有字节按顺序存储(好处:不用考虑数据类型)(下面实现用到这种,也是因为这个原因)
3.3 文件的关闭
int fclose ( FILE * stream );
这里比较简单输入要关闭的文件的文件指针,失败则返回EOF2
int main() { FILE* pf = fopen("test.txt","wb");//写的时候(wb的话会直接刷新,原来的内容消失),没有这个文件夹自动建立 if(pf == NULL) { perror("fopen"); return 1; } //....... fclose(pf); pf = NULL; }
3.4 文件的使用(这里只将二进制文件)
文件读取size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
size_t void * ptr size_t size size_t count FILE * stream
返回值:正确就匹配后面count,否则小于它 要存放文件过来的内容的起始地址 一个元素对应的字节数 读取元素个数 文件流:文件指针
文件编写size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
size_t const void * ptr size_t size size_t count FILE * stream
返回值:正确就匹配后面count,否则小于它 要传到文件中的内容的起始地址 一个元素对应的字节数 传入元素个数 文件流:文件指针
补:传入文件时,不是一个一个字节传入,而是遇到‘\n’,就刷一次传过去,或者一些特定函数,如果没有,关闭的时候会把剩余内容传过去。也就是说,如果文件没关闭,可能会有一些信息没传到文件里
键盘:stdin, 屏幕: stdout
默认打开
4.可保存的通讯录
(用二进制文件是因为我们的结构体有int型,所以在存的时候若要文本文件,我们必然是要格式化存放,这样会导致类型不匹配之类的,这样就会出错!我一开始就是因为这样错了很久🤣)
改动一:由于 屏蔽函数 所以我们需要额外一个文件
void test() { int input = 0; struct contact_list total; struct contact_list extra; init(&total, "Contact1.txt"); //当然可以建立一个doc init(&extra, "Contact2.txt");//必须在外面先建立!!! do { menu1(); printf("请做出选择:>"); scanf("%d", &input); system("cls"); switch (input) { case ADD: add_man(&total, &extra); break; case DEL: del_man(&total); break; case SEARCH: search_man(&total); break; case MODIFY: modify_man(&total); break; case SHOW: show_all(&total); printf("展示成功\n"); break; case SORT: sort_all(&total); break; case EXIT: //退出时保存!!! keep(&total, "Contact1.txt"); keep(&extra, "Contact2.txt"); destroy(&total, &extra); printf("退出成功\n"); break; case SECRET: secret(&total, &extra); break; case SHOW_SECRET: show_secret(&total, &extra); break; case REMOVE_SECRET: remove_secret(&total, &extra); break; default: printf("输入错误\n"); break; } } while (input); }
增加函数 :keep保存函数
void keep(const struct contact_list* pt, char* str) { FILE* pf = fopen(str, "wb"); if (NULL == pf) { perror("保存失败"); return; } int i = 0; fwrite(&pt->sz, sizeof(int), 1, pf); for (i = 0; i < pt->sz; i++) { fwrite(pt->list + i, sizeof(struct content), 1, pf); } fclose(pf); pf = NULL; }
改动二:初始化函数,变为从文件内容中提取 (要对第一次录入信息这种情况进行甄别)
void init(struct contact_list* pt, char* str) { assert(pt); pt->sz = 0; // memset(pt->list, 0, (MAX + 1) * sizeof(struct content)); FILE* pfr = fopen(str, "rb"); if (NULL == pfr) { perror("init"); return; } int count = 0; fread(&count, sizeof(int), 1, pfr); int i = 0; struct content* ptr = (struct content*)malloc((count + 1) * sizeof(struct content)); if (NULL == ptr) { perror("init"); return; } pt->list = ptr; pt->capacity = count + 1; pt->sz = count; for (i = 0; i < count; i++) { fread(pt->list + i, sizeof(struct content), 1, pfr); } fclose(pfr); pfr = NULL; }
其他不需要更改,我们这是增加了,保存与提取的动作!要认真分析才能够完成!
文章到此结束!谢谢观看 —>内容参考【比特科技】
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆!
这是我的代码仓库!(在马拉圈的22.12里)代码仓库
邮箱:2040484356@qq.com
在未来的博客我会完善补充! ↩︎
EOF = -1 ↩︎