目录
📌📌📌为什们要使用文件
我们平常写的代码都是存储在电脑的内存中,一旦程序运行结束,内存回收,数据也跟着丢失,等下次再次运行程序,找不到的上次
的数据,如果想要数据能够持久化的保存,就要使用文件了
📌📌📌什么是文件
磁盘(硬盘)上的文件是文件
在程序设计中,一般分为两种文件:程序文件和数据文件(从文件的功能角度分类)
📍程序文件:
通俗点就是我们写的代码,程序文件包括
源文件(后缀名.c),
目标文件(windows环境后缀.obj),
可执行文件(windows环境后缀为.exe)
打开我们写的C语言文件都可以看到这些文件
📍数据文件:
文件不一定全是我们写的程序,也可能存储数据,而数据文件就是程序运行时需要进行读写的件,比如:程序需要从某个文件中读取
数据,或者是将程序里的数据写到文件的这些文件就被称为数据文件
我们之前的学习大部分都是从终端的键盘上读取数据,输出打印到显示器屏幕上,输入对象是键盘,输出对象是显示器,而现在学的文件
就是将数据存到磁盘的文件中,需要的时候在从磁盘的文件中去读取数据存储到内存中,输入输出对象都是文件
📍文件名
每一个文件都有其独特的标签来区分文件即文件名
文件名组成:文件路径 + 文件名主干 + 文件后缀
例如: c:\code\test.txt文件路径: c:\code\文件主干: test文件后缀名: .txt
📌📌📌二进制文件和文本文件
根据文件的组织形式,数据文件可分为二进制文件和文本文件
📍二进制文件
数据以二进制形式在内存中存储,如果不加以转换地直接以二进制的形式存储到文件中,该文件就是二进制文件
📍文本文件
![](https://i-blog.csdnimg.cn/blog_migrate/e174f39b792a3061628ad4f47193988d.png)
现在已经把10000以二进制形式存储到test文档中了,如果我们在磁盘上以记事本打开这个文档,发现我们啥也看不懂,因为10000是以二进制存储进去,而我们利用文本编译器打开,他是看不懂的,所以就打印出乱码
不过我们可以利用VS来查看,具体操作步骤如下:
上述的test文件就是二进制文件
想这种就是文本文件
📌📌📌流
我们的程序有时要从各种各样的外部设备读取数据,有时需要将数据输出到各种外部设备上,而不同的设备对应的操作方法各有不同,为了方便程序员操作,抽象出流的概念,流可以理解为流淌着字符的河流
程序员只需要关心怎么把从流中读取数据及如何将数据输出到流中 ,其他操作有C语言的底层逻辑实现,这样大大简化了编程难度
📍标准流
- stdin - 标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据(键盘)
- stdout - 标准输出流,大多数的环境中输出值至显示器界面,printf函数就是将信息输出到标准输出流中(屏幕)
- stderr - 标准错误流,大多数环境中输出到显示器界面(屏幕)
📍文件指针
每个打开的文件都会在内存中开辟一块文件信息区,用来存储文件的相关信息(文件名字,文件状态,文件当前位置等),这些信息存储在一
个结构体变量中,该结构体类型是由系统声明的,取名 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* pf//文件指针
定义pf是一个指向文件信息区(一个FILE类型的结构体)的文件指针,通过pf文件指针就可以访问结构体里面的成员,从而间接找到该文件,
即文件指针能够间接地找到与之关联的文件
📌📌📌文件的打开和关闭
文件再进行读写前,应该先打开文件,结束使用后在关闭文件
//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );
举例:
//打开⽂件
FILE * pf = fopen( "test.txt", "w" );
//关闭⽂件
fclose ( "pf" );
- fopen:文件指针,存储返回的文件信息区的地址
- filename:文件名
- mode:文件打开模式
- stream:与要关闭的文件相关的文件指针
以下是文件的各种打开模式
📌📌📌文件的顺序读写
🚨fputc
使用
🚨fgetc
使用:
🚨fgets
- 从文档中读取字符串到数组中
- string 为目标空间首地址
- 返回值返回目标空间首地址
- 从流中读取字符,并将它们作为 C 字符串存储到 string 中,直到读取 (num-1) 个字符或到达换行符或文件末尾,以先发生者为准
- 换行符使 fgets 停止读取,但它被函数视为有效字符,并包含在复制到 str 的字符串中
- 成功后,该函数返回 string
- 如果发生读取错误,则设置错误指示符 (ferror)为0,并返回 null 指针
第一次读取文档中的第一行时,因为第一行字符个数大于实际读取个数9,所以第一次读取会先读9个字符,等第二次读取时,从第9个往后继续读取4个h,之后遇到换行符第二次读取结束,往后的读取都是先遇到换行符读取结束
🚨fputs
- 将 string指向的字符串复制到文档中
- 该函数从指定的地址 string 开始复制,直到到达终止 null 字符 ' \0 '
- 成功后,将返回一个非负值
出错时,该函数返回 EOF 并设置错误指示器 为0
使用:
如果想要在文档 中换行,应在要传的字符串末尾 + 换行符
🚨fscanf
- 从指定的流中读取格式化的数据
我们发现fscanf函数与我们之前学的scanf 相似;所以我们可以对比一下两个函数的参数
可以发现,fscanf 函数就比scanf 函数多了一个参数,文件流 stream,所以理论上会用scanf 函数就会用 scanf 函数
使用:
🚨fprintf
- 把数据以格式化的形式打印到指定的流上
对比一下fprintf 和 printf 两个函数的参数
fprintf 函数比 printf 函数多了一个参数,所以用法大致与printf 函数一样
使用:
还有要注意fscanf 和fprintf 函数适用于所有流,包括文件流及标准流,不仅限于文件流
在前一个用例基础上,我们把 printf 改成fprintf 来进行打印,照样可以打印到屏幕上
只需改变参数stream ,指定要具体打印到哪一个流上,sscanf 也是一样
💕对比两组函数
scanf/fscanf/sscanf
printf/fprintf/sprintf
💕sprintf
- 将格式化数据写入字符串buffer
- 用法与printf/fprintf用法类似
💕sscanf
- 从字符串中格式化读取数据
- 用法与scanf/fscanf用法类似
使用:
所有根据以上所学到的函数可以总结出以下几点
- scanf:从标准输入流上读取格式化数据
- fscanf:从指定的输入流上读取格式化数据
- sscanf:从字符串中读取格式化数据
- printf:把数据以格式化的形式打印到标准输出流
- fprintf:把数据以格式化的形式打印到指定输出流
- sprintf :把数据以格式化的形式打印到字符串
上面讲到的函数针对的都是文本
而下面讲的函数针对的是二进制
🚨fwrite
- 从buffer指向的数组中复制count 个 size个字节大小的内容到指定的文件流中
- count 为元素个数
- size 为每个元素的大小
使用:
现在再以二进制的形式读取数据
🚨fread
- 从指定的文件流中读取count 个 大小为size 的字节内容复制到字符串buffer里面
- count 为元素个数
- size 为每个元素的大小
使用:
文档里面依旧存的是刚才的二进制内容,现在以二进制的方式读出来
📌📌📌文件的随机读写
🚨fseek
- 第一个参数
stream
是对应的文件流 - 第二个参数
offset
是相对于第三个参数origin
的偏移量,向左偏移为负数,向右偏移为正数 - 第三个参数
origin
是第二个参数偏移量所参考的起始位置,有以下三个选项选项 起始偏移位置 SEEK_SET 文件的开始位置 SEEK_CUR 文件指针当前指向的位置 SEEK_END 文件的结尾
使用:
🚨ftell
![](https://i-blog.csdnimg.cn/blog_migrate/902f84ab6bb90aa17e8afa8774c3c2c2.png)
🚨rewind
![](https://i-blog.csdnimg.cn/blog_migrate/7b9197ed3af94a5fb777879bc87409ef.png)
文件读取结束的判定
- fgetc 判断是否为 EOF .
- fgets 判断返回值是否为 NULL .
- fread判断返回值是否小于实际要读的个数。
🚨feof
作用:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件末尾结束
如果返回一个非零的值说明文件是正常读取遇到了文件结束标志而结束的
🚨ferror
作用:当文件读取结束的时候,判断是读取结束的原因是否是:读取是否发生错误
检查错误指示器,如果返回一个非零的值说明是在文件读取过程中出错而导致文件读取结束的
打开一个流时,这个流上面有两个标记值:
- 是否遇到文件末尾 ---- feof
- 读取是否发生错误 ---- ferror
那个标记值发生改变,文件读取结束的原因与之相关
使用:
#include <stdio.h> #include <stdlib.h> int main(void) { int c; // 注意:int,⾮char,要求处理EOF FILE* fp = fopen("test.txt", "r"); if(!fp) { perror("File opening failed"); return EXIT_FAILURE; } //fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOF while ((c = fgetc(fp)) != EOF) // 标准C I/O读取⽂件循环 { putchar(c); } //判断是什么原因结束的 if (ferror(fp)) puts("I/O error when reading"); else if (feof(fp)) puts("End of file reached successfully"); fclose(fp); }
📌📌📌文件缓冲区
ANSIC 标准采用“缓冲文件系统” 处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。 如果从磁盘向计算机读入数据,则 从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区 (程序变量等)。缓冲区的大小根据C编译系统决定的
💕💕💕今天就介绍到这,如有错误,望大佬矫正!制作不易,望三连支持一下💕💕💕