前言
这次的博客,可以让大家快速掌握文件操作,方便大家快速找到不懂的内容
文件操作的作用以及基础
1. 为什么使用文件?
如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失 了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件
2. 什么是文件?
磁盘(硬盘)上的文件是文件。 但是在程序设计中,我们⼀般谈的⽂件有两种:程序文件、数据文件(从文件功能的角度来分类的)
3.文件名
⼀个文件要有⼀个唯⼀的文件标识,以便用户识别和引用 文件名包含3部分:文件路径+文件名主干+文件后缀 例如: c:\code\test.txt 为了方便起见,文件标识常被称为文件名
4.⼆进制文件和文本文件
根据数据的组织形式,数据文件被称为文本文件或者⼆进制文件。 数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的文件中,就是⼆进制文件 。如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
5.流
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出 操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流 想象成流淌着字符的河。 C程序针对文件、画面、键盘等的数据输⼊输出操作都是通过流操作的。 ⼀般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。
6.标准流
那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语言程序在启动的时候,默认打开了3个流:
• stdin - 标准输入流,在大多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。 • stdout - 标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出流中
• stderr - 标准错误流,大多数环境中输出到显示器界面。
这是默认打开了这三个流,我们使用scanf、printf等函数就可以直接进行输入输出操作的。 stdin、stdout、stderr 三个流的类型是: FILE * ,通常称为文件指针。 C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的
7.文件指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。 每个被使用的文件都在内存中开辟了⼀个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名 FILE.
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。 每当打开⼀个文件的时候,系统会根据文件的情况自动创建⼀个FILE结构的变量,并填充其中的信息,使用者不必关心细节。 ⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便
文件操作的使用
这里的讲解包括很多,慢慢来,前面的不懂没关系,我们直接看代码就好
文件的打开和关闭
我们使用fopen()函数打开 文件 使用fclose()关闭文件
再此之前可以看看他们的定义
FILE *fopen( const char *filename, const char *mode );
int fclose( FILE *stream );
看看使用吧,
FILE *fopen( const char *filename, const char *mode );
第一个参数是流,是一个文件指针 第二个参数是模式下面代码是"w"为只写模式
int main()
{
FILE* pl= (FILE*)fopen("text.txt", "w");
char a[20] = "hello world";
char* p = a;
while(*p!='\0')
fputc(*p++,pl);
fclose(pl);
return 0;
}
在这里提醒一句,在只写模式中自能写入信息,不能读信息,待会还会在提醒,别急
看结果
绝对路径与相对路径
这个代码就是通过while循环,把一个字符串一个一个字符的存储在文件text.txt中
fopen的第一个参数为文件名其实在这里是相对路径,还可以使用绝对路径来作为参数
其实使用绝对路径更加严谨,因为相对路径是相对于本电脑而言的路径,绝对路径也不难,
可以从文件夹中复制出来,就拿刚刚生成的文件来试水
红方框所包的位置就是文件的绝对路径,其实很简单,找到文件,然后查看文件的属性
我们在把它写入代码中看看能否成功
但这里注意,位置给的是前面路径,最后还要加上自己的文件名
emmmm 失败了 为什么呢?
原因是转义字符,我们的 \t \ddd 在这里d指的是数字呦 比如 \5 \6 \123 \24
这里是表示最少一位最多三位的8进制的数字
ok 我们现在在所有的转义字符中在多加入一个\就可以解决问题了
这样就成功了
文件的读写模式
我们就直接打出来把
其实这是由规律的,发现没
1 有后缀b就是二进制文件
2 r 读 w 写 a追加 记住就好 后缀+就是读写
3r只能读文件,一旦文件不存在 报错,但是w 和a可以创造文件 当它不存在时
4w(只写)每次打开文件时会把原文件的内容覆盖,然后重新写入
5 a追加 r 只读不会改变原有的文件,追加时是在原有的内容上追加的
最后大家要自行尝试各种模式下的文件操作
接下来要讲解一些相关的输入输出函数
函数名 功能 适用于
fgetc 字符输入函数 所有输入流
fputc 字符输出函数 所有输出流
fgets 文本行输入函数 所有输入流
fputs 文本行输出函数 所有输出流
fscanf 格式化输入函数 所有输入流
fprintf 格式化输出函数 所有输出流
fread ⼆进制输入 文件输入流
fwrite ⼆进制输出 文件输出
int fgetc(FILE *pf);
int fputc(int n,FILE *pf);
char*fgets(const char *a,size_t num,FILE *pf);
char *fputs(const char *a,FILE * pf);
int fread(cosnt char *buf,size_t typesize,size_t elementnums,FILE* pf);
int fwrite(const char *buf,size_t typesize,size_t elementnums,FILE* pf)
单个字符处理函数
fgetc 字符输入函数 所有输入流
fputc 字符输出函数 所有输出流
看代码
//fputc
int main()
{
FILE* pf = fopen("BoKe.txt", "w");
//1 fputc 针对文件流
char a[20] = "djlkawj";
char* p = "dkakdkla";
for (int i = 0; i < strlen(a); i++)
{
fputc(a[i], pf);
}
//2fputc 针对屏幕(标准输出流)
while (*p != '\0')
{
fputc(*p++, stdout);
}
fclose(pf);
}
看结果
看fgetc
int main()
{
FILE* pf = fopen("BoKe.txt", "r");
int ch = 0;
//注意要打括号,赋值操作符的优先级小于逻辑操作符
//遍历文件流所有内容
//针对文件流
while((ch = fgetc(pf))&&ch!=EOF)
putchar(ch);
fclose(pf);
printf("\n");
int a = 0;
//针对标准输入流
while ((ch = fgetc(stdin))&&ch!= EOF)
{
putchar(ch);
}
return 0;
}
行字符处理函数
char*fgets(const char *a,size_t num,FILE *pf);
char *fputs(const char *a,FILE * pf);
直接看代码
int main()
{
//fputs 针对文件流
FILE* pf = fopen("BoKe.txt", "w");
char buffer[20] = "jdakldjad";
fputs(buffer, pf);
char buffer2[20] = "jdalkdajjadlk";
fputs(buffer, pf);
//针对 输出流
fputs(buffer, stdout);
fputs(buffer2, stdout);
fclose(pf);
//fgets 针对文件流
pf = fopen("BoKe.txt", "r");
char buffer3[20];
fgets(buffer3, 10, pf);
printf("%s\n", buffer3);
//针对输出流
char buffer4[20];
fgets(buffer4, 10, stdin);
printf("%s\n", buffer4);
return 0;
}
看结果
值得注意的是在fgets中有一个限制大小的参数,该参数可以防止数组越界,并且注意的是在给数组值时该限制长度是字符串的长度
并且他不像gets puts一样可以自带换行,注意他不会自动换行
而且遇到换行不会结束,只有遇到\0或者文本结束符号ctrl+z才会结束
这个千万不要搞错了
格式化输入输出函数
fscanf 格式化输入函数 所有输入流
fprintf 格式化输出函数 所有输出流
int main()
{
student s1 = { 20,"zhangsan",80 };
FILE* pf = fopen("Test.txt", "w");
fprintf(pf,"%d %s %d",s1.aag,s1.name,s1.score);
fclose(pf);
pf=fopen("Test.txt", "r");
student s2 = { 0 };
fscanf(pf, "%d %s %d", &(s2.aag), s2.name,&(s2.score));
printf("%d %s %d\n", s2.aag, s2.name, s2.score);
fclose(pf);
pf=fopen("Test.txt", "wb");
student arr[3] = { {20,"zhangsan",56},{25,"lisi",15},{65,"haha",99} };
fwrite(arr, sizeof(student),3, pf);
fclose(pf);
return 0;
}
只针对文件流的输入输出
fread ⼆进制输入 文件输入流
fwrite ⼆进制输出 文件输出
我们仍然看上面的代码
此时文件中存储了,
student arr[3] = { {20,"zhangsan",56},{25,"lisi",15},{65,"haha",99} };
三个结构体成员,我们要取得最后一个结构体元素的内容怎么办
通过一个fseek()找到最后一个结构体元素的位置,也就是文件指针
的指向位置,在通过fread读出该结构体
当然 fwrite的使用就更加简单了,先看fseek和fread的联动
int main()
{
FILE *pf = fopen("Test.txt", "rb");
student s3 = { 0 };
fseek(pf, -(long)sizeof(student), SEEK_END);
fread(&s3, sizeof(student), 1, pf);
printf("%d %s %d\n", s3.aag, s3.name, s3.score);
return 0;
}
这个代码就是可以取出元素
看看fwrite
#include <stdio.h>
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//⼆进制的形式写到⽂件中
fclose(pf);
pf = NULL;
return 0;
}
fread 和fwrite都一样
4个参数 第一个是要取或读的地址,第二个是类型所占的字节大小 第三个是文件流
而且必须是文件流
当然这里是一定是二进制的,fwrite 是把内容以二进制写入文件
fread是把内容读入一个被指向的地址中
总算结束了,希望有所帮助吧