文件操作
💛💛笔记自取~ :文件操作笔记💛💛
❤️欢迎喜欢学习C/C++的朋友互关一起努力!!❤️
文章目录
导言
前几天发布了有关通讯录的文章,在输入我的联系人信息后,如果退出了程序,我的联系人信息就会消失。我们想要的通讯录可能不是这样,我们希望的是通讯录可以一直保存着我们的联系人信息
通讯录文章指路:简易通讯录实现,gitee上还更新了动态内存版本的通讯录
本文将介绍,通过文件操作,保存我们的联系人信息
一、常见文件类型
文件的分类如下图
1. 程序文件
🌟源程序文件(后缀为.c)
🌟目标文件(windows环境后缀为.obj)
🌟可执行程序(windows环境后缀为.exe)
2. 数据文件
✨有时我们运行程序时候需要读取数据,进行内容的输出
3. 文件名
✨相信文件名大家都很熟悉是如何构成的
文件路径+文件名主干+文件后缀
- 例如: c:\code\test.txt
二、文件指针
❗️❗️操作文件步骤:
- ⭐️打开文件
- ⭐️读取/写入数据
- ⭐️关闭文件
🌟我们每次打开文件时,都会有个指针指向一个
文件信息区
,用来存放文件的相关信息(如文件名、文件状态及文件当前的位置等)🌟这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,名字叫
FILE
实际上在vs2019中,corecrt_wstdio.h
头文件中就有描述
vs2019可能封装的很深,不太好观察
我们来看看vs2013,在stdio.h
头文件中有描述
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
这样我们就能看的很清楚,里面存放的都是什么数据
✨每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE
结构的变量,并填充其中的信息
✨一般都是通过一个FILE的指针
来维护这个FILE结构的变量,这样使用起来更加方便
✨
定义f
是一个指向FILE类型数据的指针变量
。可以使pf指向某个文件的文件信息区(是一个结构体变量)文件指针变量能够通过文件信息区找到与它关联的文件。
三、文件的打开和关闭
1. fopen打开文件
//打开文件
FILE * fopen (const char * filename, const char * mode);
❗️❗️如果打开失败,返回空指针
❗️❗️
filename
是文件名mode
是打开方式
✨✨mode打开方式
有什么样的类型呢
文件使用方式 | 含义 | 如果指定文件不存在 |
---|---|---|
“r”(只读) | 为了输入数据 ,打开一个已经存在的文本文件 | 出错 |
“w”(只写) | 为了输出数据 ,打开一个文本文件 | 建立一个新的文件 |
“a”(追加) | 向文本文件尾 添加数据 | 建立一个新的文件 |
“rb”(只读) | 为了输入数据 ,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据 ,打开一个二进制文件 | 建立一个新的文件 |
“ab”(追加) | 向一个二进制文件尾添加数据 | 出错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,建议一个新的文件 | 建立一个新的文件 |
“a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+”(读写) | 为了读和写打开一个二进制文件 | 出错 |
“wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
“ab+”(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
本文主要是介绍和了解前三个方式
2. "r"读取文件
先来试试读取文件
我先在桌面新建一个txt文件
按照打开文件的代码书写,注意防止空指针
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE * f = fopen("file.txt", "r");
if (f == NULL)
{
perror("fopen");//判断打开成功
return -1;
}
return 0;
}
❗️❗️结果是它告诉我没有这个文件!
❗️❗️❗️注意:这种只写名称的写法叫传递相对路径
地址,如果只写文件名称,该文件必须在源文件同一路径
下才能读取成功
🌟我们需要书写它的全部路径,这种写法叫传递绝对路径
地址
右击这个文件,找到属性,找到它的路径
🌟修改代码:注意这里\和字母连起来会造成转义字符
,所以需要在前面添加\为了反转义
int main()
{
FILE * f = fopen("C:\\Users\\yujing wang\\Desktop\\file.txt", "r");
if (f == NULL)
{
perror("fopen");
return -1;
}
return 0;
}
💢💢💢运行之后,我仍然读取失败??
找了半天才知道原因在哪
🔥🔥🔥原来我的电脑在新建文件的时候,不用自己给它定义是什么类型文件,它会自动添加,而我写的file.txt才是文件名,txt才是文件类型,所以在查找它路径的时候顺便看一下名称是否正确
这次我们不修改代码了,直接修改文件名
🔥这次读取成功
了!
我们在文件里输入file
这几个字符,试试如何把读取的字符打印
出来
fgetc 读取字符函数
参数和返回值
int fgetc( FILE *stream );
int main()
{
FILE * f = fopen("C:\\Users\\yujing wang\\Desktop\\file.txt", "r");
if (f == NULL)
{
perror("fopen");
return -1;
}
int ret = fgetc(f);
printf("%c", ret);
ret = fgetc(f);
printf("%c", ret);
return 0;
}
运行结果,如我们所愿
3. fclose关闭文件
//关闭文件
int fclose ( FILE * stream );//传的是文件信息区的地址
直接利用上面打开文件的代码,在后面添加关闭文件
✨✨✨注意用完后指针指向的地方没有变,需要把它变成空指针✨✨✨
int main()
{
FILE * f = fopen("C:\\Users\\yujing wang\\Desktop\\file.txt", "r");
if (f == NULL)
{
perror("fopen");
return -1;
}
fclose(f);
f = NULL;
return 0;
}
4. "w"写入文件
💗我们把刚才桌面建的file文件删掉
,再执行原来的代码,但打开方式是"w"
int main()
{
FILE * f = fopen("C:\\Users\\yujing wang\\Desktop\\file.txt", "w");
if (f == NULL)
{
perror("fopen");
return -1;
}
fclose(f);
f = NULL;
return 0;
}
结果是
✨桌面上多了刚刚新增的文件,如果在文件中写东西,再运行代码,文件又会变成空文件
,很神奇
我们还能在代码里面给file文件添加字符
fputc字符输入函数
参数和返回值
int fputc( int c, FILE *stream );
把文件地址传进去,给文件写入字符
int main()
{
FILE * f = fopen("C:\\Users\\yujing wang\\Desktop\\file.txt", "w");
if (f == NULL)
{
perror("fopen");
return -1;
}
fputc('f', f);//字符输入函数
fputc('i', f);
fputc('l', f);
fputc('e', f);
fclose(f);
f = NULL;
return 0;
}
运行后我们打开file文件
我们用代码给文件中写入了file
四、文件的顺序读写
顺序读写函数表
功能 函数名 适用于 字符输入函数 fgetc 所有输入流 字符输出函数 fputc 所有输出流 文本行输入函数(读取包含’\0’) fgets 所有输入流 文本行输出函数 fputs 所有输出流 格式化输入函数(按照一种格式读取) fscanf 所有输入流 格式化输出函数 fprintf 所有输出流 二进制输入 fread 文件 二进制输出(按二进制写入) fwrite 文件 ✨外部设备:文件、屏幕、网络、光盘、软盘
✨而流是指:可以读写到某个设备
c语言程序运行起来就
默认打开
3个流标准输出流 - stdout(屏幕打印,这里屏幕就是编译器调试控制台)
💫标准输入流 - stdin(键盘)
💫标准错误流 - stderr
💫这几个流类型都是FILE*
前面已经介绍了字符输入函数fgetc
和字符输出函数fputc
1. fputs 文本行输出函数
参数与返回值
int fputs( const char *string, FILE *stream );
传参传,写入的文本行和文件的地址
成功返回0
首先桌面上没有这个文件,我们需要写一个文件,写入字符串内容为file
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "w");
if (p == NULL)
{
perror("fopen");
return -1;
}
fputs("file", p);
fclose(p);
p = NULL;
return 0;
}
运行结构是,桌面上多出个文件,test.txt,内容为file,比之前一个一个字符输入要简单多
在这个代码运行完的基础上,我们读取一下这个字符串吧
2. fgets 文本行输出函数
参数与返回值
char *fgets( char *string, int n, FILE *stream );
传参:存放字符串的位置,传多少字节(最多不会超过最字节数),文件地址
ps:这里传的字节中默认
包含'\0'
成功返回string的起始地址
直接上代码看吧:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "r");
if (p == NULL)
{
perror("fopen");
return -1;
}
char arr[5] = { 0 };
char* ret = fgets(arr, 5, p);
printf("%s\n", arr);
printf("%s\n", ret);
fclose(p);
p = NULL;
return 0;
}
运行结果:
两种打印方式均可
3. fprintf 格式化输出函数
参数和返回值
int fprintf( FILE *stream, const char *format [, argument ]...);
传参:文件地址,格式化内存(此格式化,为该内容有一定的格式)
类似于
printf的""中的功能
返回值:成功返回写入几个字节
代码与前几个类似
struct stu
{
int a;
char b[4];
}st = {233, "abc"};
int main()
{
FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "w");
if (p == NULL)
{
perror("fopen");
return -1;
}
int ret = fprintf(p, "%d %s", st.a, st.b);
printf("%d", ret);
fclose(p);
p = NULL;
return 0;
}
效果:
4. fscanf 格式化输入函数
参数和返回值
int fscanf( FILE *stream, const char *format [, argument ]... );
传参:文件地址,格式化内存(此格式化,为该内容有一定的格式)
根据上面已经输出的代码,我们来输入进结构体中
#include <stdio.h>
#include <stdlib.h>
struct stu
{
int a;
char b[4];
}st;
int main()
{
FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "r");
if (p == NULL)
{
perror("fopen");
return -1;
}
fscanf(p, "%d %s", &(st.a), st.b);
printf("%d %s\n", st.a, st.b);
fclose(p);
p = NULL;
return 0;
}
运行结果:
5. fwrite 二进制输出函数
参数和返回值
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
参数:指针指向要被写入的数据,一个元素的字节大小,最大的元素个数,文件地址
返回值:真正被书写入的元素个数
实验一下:
struct stu
{
int a;
char b[4];
}st = {233, "abc"};
int main()
{
FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "wb");
if (p == NULL)
{
perror("fopen");
return -1;
}
int ret = fwrite(&st, sizeof(st), 1, p);
printf("%d", ret);
fclose(p);
p = NULL;
return 0;
}
运行结果:
这里字母按照二进制形式写进去就是字母,而二进制写入数字我们看不到
6. fread 二进制输入函数
参数与返回值
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
参数:指针指向读取的数据放置的位置,一个元素的字节大小,最大的元素个数,文件地址
也是按照上面代码读取
struct stu
{
int a;
char b[4];
}st;
int main()
{
FILE* p = fopen("C:\\Users\\yujing wang\\Desktop\\test.txt", "rb");
if (p == NULL)
{
perror("fopen");
return -1;
}
fread(&st, sizeof(st), 1, p);
printf("%d %s", st.a, st.b);
fclose(p);
p = NULL;
return 0;
}
看来读取成功了
小结
看到这已经疑惑很久了,🌟fprintf和printf到底有什么区别,是不是有什么联系呢
scanf/fscanf/sscanf
printf/fprintf/sprintf
这两组非常相似的函数到底是什么
函数 | 作用 |
---|---|
scanf | 从标准输入流(键盘)读取格式化的数据 |
fscanf | 从所有的输入流读取格式化的数据 |
sscanf | 从字符串中读取一个格式化的数据(把一个字符串转换成一个格式化的数据) |
printf | 把格式化的数据输出到标准输出(屏幕)上 |
fprintf | 把格式化的数据输出到所有输出流(屏幕/文件)上 |
sprintf | 把格式化的数据转换成对应的字符串! |
虽然我们日常不会用程序去写一个txt文件,但我们需要知道这种操作原理,
🌟在必要的时候,也会带来便利!
我有百度搜索过
其实它的例子就隐藏在我们生活中😆😆😆~~