前言:
为什么要学习文件操作:
1、如果大家写过一些代码,当运行结束的时候,这些运行结果将不复存在,除非,再次运行时这些结果才能展现在屏幕上面,就比如之前写过的通讯录。
现实中的通讯录可以保存自己写进去的信息,下次方便查找,但是我们写的当程序推出结束后,里面的信息也随之销毁不会保存。
2、 是因为我们运行的结果都存在了内存当中,程序结束后内存中的数据将会自动清空,但是我们找到我们保存在C盘或者是其它盘中的文件数据就算电脑关机也不会被清除,原因是这些文件数据都保存在硬盘中,硬盘中的数据不会自动清空,所以一般我们删除一些文件都是手动删除。
所以学会文件的相关操作以后,数据就可以直接保存在硬盘当中!!!
文件的分类:
我们经常看到不同的文件,但是在程序设计的时候文件有以下两种:
1、程序文件:
常以 .c \ .obj \ .exe结尾的
2、数据文件:
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件, 或者输出内容的文件。
例如:
文本文档.txt,
文件名:
一个完整的文件,肯定是有完整的文件名,文件名有如下几部分组成:
文件路径+文件名主干+文件后缀例如: c:\code\test1.txt
文件的打开与关闭:
我们向文件中存储数据的步骤应该是:
1、打开文件。
2、输入要存入的数据。
3、关闭文件。
1、打开文件:
当我们打开文件前vs会提供给我们一个FILE类型的结构体:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
编译器会开辟一个文件信息区,这些文件的地址,名称,大小等等的一些信息会保存在这个地方,不同编译器提供的文件信息可能有所不同。
我们通常会创建一个FILE*的指针维护文件信息,当打开文件的时候,会返回文件的相关信息的地址,文件的相关内容我们用FILE*去接收。
fopen打开文件:
要打开一个文件,我们通常用fopen函数打开,需要包括fopen相关的头文件
#include<stdio.h>
当然fopen打开文件有如下的打开方式:
具体打开代码如下:
#include<stdio.h>
int main()
{
FILE* pf = fopen("cool.txt", "w");
return 0;
}
对fopen传参的说明:
第一个" " 里面写你要打开的文件的文件名,有两种写法:
第一种相对路径打开:如果文件名称前面什么都不加,那就表示在当前程序存放的路 径下打开
如果加..\\表示在上一级路径下打开..\\..\\表示在上上级路径下打开
第二种绝对路径打开:写入完整的名称,也就是完整的文件路径,从根目录到该文件 的路径
例如:D:\cold\2024_6_16_\2024_6_16_第二个" "里面写的是你要用哪种方式打开文件
如果要用写("w")的形式打开,如果相路径中没有对应的文件,就会自己创建一个
当然也有可能文件打开失败,所以要判断一下,fopen函数打开失败的时候会返回NULL
指针:
所以代码改造为:
int main()
{
FILE*pf = fopen("cool.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
return 0;
}
2、关闭文件
关闭文件我们用fclose关闭。
传参只需要传需要关闭文件的的地址。
代码如下:
fclose(pf);
但是关闭完之后,pf变量中存的文件地址是不是还存在呢?
答案是存在的,这是pf中存的地址不再是我们开辟的了,导致FILE*pf变成了野指针,所以最后要将pf == NULL。
代码改造如下:
fclose(pf);
pf = NULL;
完整文件打开与关闭代码:
int main()
{
FILE*pf = fopen("cool.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
文件的读写操作:
介绍完文件如何打开与关闭后,需要我们知道如何将我们想要将内容写入文件中。
我们需要知道几个函数和相应的功能
流的概念:
数据和外部设备进行相互交互的时候,都是通过流(数据流)进行交互。
并不是直接进行信息交换的!
传输不同的数据的时候,我们需要把数据传输到不同的流当中,当然在c语言运行的时候,编译器会自动打开三个标准流,使得我们可以直接传输:
1、标准输入流:stdin
2、标准输出流:stdout
3、标准错误流:stderr
在我们使用printf在屏幕上打印一些数据的时候就要用到标准输出流。
在我们使用scanf从键盘上获取数据到内存中的时候就要用到标准输入流。
这里的流也可以理解为FILE*的指针!
有上述一些输入输出函数和流的基本概念,接下来我们一个一个探讨和举例。
fgetc和fputc函数
fputc字符输出函数
使用如下:
int main()
{
FILE* pf = fopen("cool.txt", "w");//写入文件要用"w"
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputc('a',pf);//输出字符
fclose(pf);
pf = NULL;
return 0;
}
两个参数:
第一个参数:传入想写的字符。
第二个参数:是写你要通过哪个流传入,也就是文件的地址。
效果如下:
那如果我想要穿多个字符进去,该怎么传进去?
在使用fgetc的时候每传进去一个字符,它的光标自动就会往右移一个位置,等待下一个数据传入。
int main()
{
FILE* pf = fopen("cool.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputc('a',pf);
fputc('b', pf);
fclose(pf);
pf = NULL;
return 0;
}
cool.txt文件中就不会打印出ab呢?
最终会打印ab,也就证实了每当用fgetc输出一个字符,对应的字符指针(光标)会自动往后移一个。
可以用循环打印字母表:
int main()
{
FILE* pf = fopen("cool.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char pc = 0;
for (pc = 'a'; pc <= 'z'; pc++)
{
fputc(pc, pf);
}
fclose(pf);
pf = NULL;
return 0;
}
当然此时我不想让它输出到文件中,我想让它输出到屏幕上,我们可以用标准输出流:
代码如下:
int main()
{
FILE* pf = fopen("cool.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char pc = 0;
for (pc = 'a'; pc <= 'z'; pc++)
{
fputc(pc, stdout);//文件的地址换成标准输出流
}
fclose(pf);
pf = NULL;
return 0;
}
输出结果:
fgetc字符输入函数:
前提:
在用这个函数的时候打开文件的时候我们需要将打开方式变为只读的形式,一定要相互匹配。
此时我在txt文件中输入abcd。
可以用fgtec读取并存入变量中,代码如下:
int main()
{
FILE* pf = fopen("cool.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int arr = fgetc(pf);
printf("%c\n", arr);
fclose(pf);
pf = NULL;
return 0;
}
fgets和fputs函数
fputs字符串输出函数:
该函数可以将字符串输出到文件中,代码如下:
int main()
{
FILE* pf = fopen("cool.txt", "w");//必须是写的形式打开文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputs("abcde", pf);
fclose(pf);
pf = NULL;
return 0;
}
其函数和字符函数fputc比较相似,只不过传入的字符变成字符串。
fgets字符串输入函数:
将字符串输入到内存中,保存在字符数组中并打印。
不一样的是,fgets参数有三个:
第一个参数是:你要讲字符串输入到什么地方,
第二个参数是:输入的字符的个数包括\0也就是你想输入3个字符,这里num必须是4,因为这4个里面有一个是\0。
第三个参数是:文件的地址。
例如:
我在文本文件中输入abcde,然后在屏幕上打印abc,我应该传入哪些参数?
代码如下:
int main()
{
FILE* pf = fopen("cool.txt", "r");//这里一定要用读的形式打开
if (pf == NULL)
{
perror("fopen");
return 1;
}
char arr[20];
fgets(arr, 4, pf);
printf("%s\n", arr);
fclose(pf);
pf = NULL;
return 0;
}
fscanf和fprintf函数
fprintf格式化输出函数:
那么它和printf的区别在哪?
fprintf有两个参数,相较于printf多了第一个参数——文件地址。
fprintf函数的使用:
int main()
{
FILE* pf = fopen("cool.txt", "w");//以写的方式打开
if (pf == NULL)
{
perror("fopen");
return 1;
}
char arr[] = { "i love world" };
fprintf(pf, "%s\n%d\n", arr,3);
fclose(pf);
pf = NULL;
return 0;
}
这个格式化输出函数传入数据就十分方便了。
想穿什么类型的数据都可以,只要格式正确。
fscanf格式化输入函数:
这里的fscanf和scanf的区别是什么呢?
注意fscanf还是需要多一个参数,文件的地址,而且,fscanf当遇到第一个非空格字符开始输入,当再次遇到空格字符就会停止输入!!!
就比如:
fscanf字符光标一直往后找,找到第一个非空格字符读取,每读一个光标往后移一个,直到再次遇到空格字符停止读取。
int main()
{
FILE* pf = fopen("cool.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char arr[20];
/*int n = 8;*/
fscanf(pf, "%s", arr);
/*fscanf(pf, "%d", &n);*/
printf("%s\n", arr);
fclose(pf);
pf = NULL;
return 0;
}
此时运行结果:
当写成这样:
运行结果:
当然要想读取不同类型的数据,我们可以将不同类型的数据用空格字符隔开,然后多次利用fscanf读取!
例如:
int main()
{
FILE* pf = fopen("cool.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char arr[20];
int n = 8;
float a = 0;
fscanf(pf, "%s", arr);
fscanf(pf, "%d", &n);
fscanf(pf, "%f", &a);
printf("%s %d %f\n", arr,n,a);
fclose(pf);
pf = NULL;
return 0;
}
fread和fwrite函数
fwrite二进制输出函数:
注:此时文档是二进制文档,文档中存的是数据的二进制
有四个参数:
第一个参数:需要传入输出的变量的地址
第二个参数:需要传入每一个变量的大小。
第三个参数:数据的个数
第四个参数:文件的地址
int main()
{
FILE* pf = fopen("cool.txt","wb");
if (pf == NULL)
{
perror("fopen");
}
char arr[] = {"abcde"};
int arr1[] = {1,2,3,4,5,6};
fwrite(arr1,sizeof(int),6,pf);
fwrite(arr, sizeof(char), 5, pf);
fclose(pf);
pf = NULL;
return 0;
}
fread二进制输入函数:
它需要的参数和fwrite是一样的,但是我们需要注意
fread的返回值类型是size_t类型,返回读取到的数据的个数!!
这里应该做一个判断。
int main()
{
FILE* pf = fopen("cool.txt","rb");//二进制读文件
if (pf == NULL)
{
perror("fopen");
}
int i = 0;
char arr[] = {"abcde"};
int arr1[] = {1,2,3,4,5,6};
fread(arr1,sizeof(int),6,pf);
fread(arr, sizeof(char), 5, pf);
for (i = 0; i < 6; i++)
{
printf("%d ", arr1[i]);
}
printf("%s", arr);
fclose(pf);
pf = NULL;
return 0;
}
sprintf和sscanf函数
sprintf函数:
是将格式化的数据转化成字符串。
例如:
struct S
{
int n;
float a;
char b;
};
int main()
{
struct S s = { 10,(float)3.14,'a' };
char arr[100];
sprintf(arr,"%d %lf %c",s.n,s.a,s.b);
printf("%s\n", arr);
return 0;
}
sscanf函数:
是将字符串中的数据转换为格式化数据
struct S
{
int n;
float a;
char b;
};
//int main()
//{
// struct S s = { 10,(float)3.14,'a' };
// char arr[100];
// sprintf(arr,"%d %lf %c",s.n,s.a,s.b);
// printf("%s\n", arr);
// return 0;
//}
int main()
{
struct S s = { 10,(float)3.14,'a' };
char arr[100];
sprintf(arr,"%d %f %c",s.n,s.a,s.b);
sscanf(arr, "%d %f %c", &(s.n), &(s.a), &(s.b));
printf("%d\n", s.n);
printf("%f\n", s.a);
printf("%c\n", s.b);
return 0;
}