C语言: 文件的读写
extra:通讯录文件版实现
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