一、首先我们要搞清楚为什么要使用文件呢?
二、什么是文件呢?
简单来说磁盘(硬盘)上的文件是文件。
数据文件:文件的内容不⼀定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件,而这次讨论文件的操作就是基于数据文件而进行的。
文件名
一个件要有⼀个唯⼀的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: D:\vs\test.txt
三、二进制文件和文本文件
根据数据的组织形式,把数据文件分为二进制文件和文本文件。
二进制文件:数据在内存中以二进制的形式存放,如果不加转换而输出到外存的文件就是二进制文件。
文本文件:如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
一个数据在文件中是怎样储存的呢?
假如我们有一个数据10000那么他在两种文件中是怎么存储的呢?我们可以看下面这张图
这时候你们可能好奇为什么以ASCII值的形式存储会占五个字节
这是因为如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2019测试)。
注:字符一律以ASCII值的形式存储,而整数类型则两种都可以存储,既可以是ASCII值类型也可以是以二进制的形式存储。
我们可以看一段小代码看是否像我们说的那样
int main()
{
int a = 10000;
FILE* pf = fopen("text.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fwrite(&a, 4, 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
我们在vs中打开它
我们可以看到他的二进制,但在vs中数据是以十进制存放的我们可以换算一下转换凡是如下
四、文件的打开和关闭
1.流和标准流
1.1、流
1.2、标准流
1.3、文件指针
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
不同的C语言编译器的类型不一定相同但是大同小异,有相同点也有相异点
![](https://i-blog.csdnimg.cn/blog_migrate/c7e84c5a957096a62203923f22599e11.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0a580dad97adb9ae2eec69220adef9b6.png)
1.4、文件的读写和关闭
文件在读写之前应该打开,结束之后应该关闭
打开用fopen
这里的mode指的是文件的打开方式
结束用fclos
更多的详细信息可以到cplusplus上查看更多信息网址如下
文件的打开方式很多如下图所示
我们来举几个例子实践一下
int main ()
{
FILE * pFile;
//打开⽂件
pf = fopen ("text.txt","w");
if(pf==NULL)
{
perror("fopen");//如果文件没打开成功打印出错误的原因
retrun 1;
}
//⽂件操作
if (pf!=NULL)
{
fputs ("fopen example",pf);//将内容写入text.txt中
//关闭⽂件
fclose (pf);
}
return 0;
}
如果没有报错那么打开text.txt会看到,这样就成功的将fopen example存入文件text.txt中了
五、文件的顺序读写
文件的顺序读写函数有很多,且两两相识,很容易记混淆
接下来我们一一了解这几种函数
5.1、fputc
参数
int main()
{
//打开⽂件
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
//关闭⽂件
fclose(pf);
pf = NULL;
return 0;
}
5.2、fgets
int main()
{
//打开⽂件
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%c\n", ch);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
ch = fgetc(pf);
printf("%c\n", ch);//c
ch = fgetc(pf);
printf("%c\n", ch);//d
//关闭⽂件
fclose(pf);
pf = NULL;
return 0;
}
5.3、fputs
int main()
{
//打开⽂件
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputs("hello word", pf);
//关闭⽂件
fclose(pf);
pf = NULL;
return 0;
}
5.4、fgets
str:指向复制读取的字符串的char数组的指针。
num:要复制到str中的最大字符数(包括终止空字符)
stream:指向标识输入流的FILE对象的指针。
stdin可以用作从标准输入读取的参数。
int main()
{
//打开⽂件
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
char arr[10] = { 0 };
fgets(arr,10,pf);
//关闭⽂件
fclose(pf);
pf = NULL;
return 0;
}
注:从流 中读取字符并将它们作为 C 字符串存储到str中,直到读取 ( num -1) 个字符或到达换行符或到达文件末尾(以先发生者为准)。 换行符使fgets停止读取,但该函数将其视为有效字符并包含在复制到str的字符串中。复制到str 的字符后会自动附加一个终止空字符。 请注意,fgets与gets完全不同:不仅fgets接受流参数,还允许指定str的最大大小并在字符串中包含任何结尾换行符。
这个时候我们就不得不讲到所有输入流和所有输出流了
我们可以对比一个组函数
scanf/fscanf/sscanf
printf/fprintf/sprintf
这里的scanf和printf就不讲了我们主要讲后面的两种
5.5、fscanf和fprintf
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "张三",20,85.5 };
//想把s中的数据存入文件中
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 0;
}
// //写文件--以文本的类型写进去的
fprintf(pf, "%s %d %f", s.name, s.age, s.score);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { 0 };
//想从text.txt中读取数据放在s中
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 0;
}
//读文件
fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
//打印在屏幕前
printf("%s %d %f", s.name, s.age, s.score);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
5.6、sscanf和sprintf
struct S
{
char name[20];
int age;
float score;
};
int main()
{
char str[200] = { 0 };
struct S s = { "张三",25,85.5f };
sprintf(str, "%s %d %f", s.name, s.age, s.score);
printf("以字符串形式%s\n", str);
struct S t = {0};
sscanf(str, "%s %d %f", t.name, &(t.age), &(t.score));
printf("按照格式打印%s %d %f\n", t.name, t.age, t.score);
return 0;
}
5.7、fwrite
int main()
{
int arr[5] = { 0 };
FILE* pf = fopen("text.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写数据
int sz = sizeof(arr) / sizeof(arr[0]);
fwrite(arr, sizeof(arr[0]), sz, pf);//以二进制方式写进去的
fclose(pf);
pf = NULL;
return 0;
}
5.8、fread
int main()
{
int arr[5] = { 0 };
FILE* pf = fopen("text.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读数据
fread(arr, sizeof(arr[0]), 5, pf);
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
fclose(pf);
pf = NULL;
return 0;
}
六、文件的随机读写
6.1、fseek
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
ch = fgetc(pf);
printf("%c\n", ch);//c
fclose(pf);
pf = NULL;
return 0;
}
6.2、ftell
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%c\n", ch);//a
fseek(pf, 0, SEEK_END);
printf("%d\n", ftell(pf));//11
fclose(pf);
pf == NULL;
return 0;
}
6.3、rewind
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%c\n", ch);//a
fseek(pf, -4, SEEK_END);
ch = fgetc(pf);
printf("%c\n", ch);//f
rewind(pf);//回到文件起始位置
ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf == NULL;
return 0;
}
七、文件读取结束的判定
![](https://i-blog.csdnimg.cn/blog_migrate/9418309f8a6516896e4db9f94cba3fcf.png)
我们来看一个例子
讲到这里文件的基本操作就已经结束了,下次再见