标啊题
想必我们日常用电脑经常打开各种各样的文件,会用,但具体形容一下....
“啊?不就是..文件吗?鼠标点开那个...”
——想必会如这般语塞吧
这不得证明一下咱可是程序员,讲的专业点 咳 进入正题
很早使用计算机的老前辈想将写的程序的数据存储在电脑的内存中,但是,如果程序退出,内存回收,数据就丢失 了,等再次运⾏程序,是看不到上次程序的数据的。
于是老前辈脑瓜子一转,如果要将数据进⾏持久化的保存,搞个叫⽂件的东西存着就好了。
放到现在,咱都知道,磁盘(硬盘)上的⽂件是⽂件。
但是在咱敲代码的眼中,我们⼀般谈的⽂件,按功能分有两种:程序⽂件、数据⽂件
包括 源程序⽂件(后缀为.c),
⽬标⽂件(windows环境后缀为.obj),
可执⾏程序(windows 环境后缀为.exe)。
也就是程序运⾏时读写的数据——比如程序运⾏需要从中读取数据的文件,或者输出内容的文件。
说的俗一点,就是咱看小说下载的那些个以.txt为后缀的东西。
这篇笔记记的也是这玩意。
用鼠标对电脑里的文件图标点击打开谁都会吧,有手就行,这个打开关闭就不用多说了。
咱重点说说用敲代码的方式,对文件进行打开关闭。
把大象关进冰箱需要几步呢? 三步——打开冰箱门,把大象放进去,关上冰箱门。
同样的,读写文件也是——
打开文件 → 读/写文件内容 → 关闭文件
首先,我们需要一个能帮我们定位输入输出文件内容的工具。
每当打开⼀个⽂件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构体的变量(这个结构体里面有文件名,文件位置等文件相关信息),需要我们⼀个FILE的指针来维护这个FILE结构的变量。
在我的个人理解里,他像一个编辑定位的鼠标光标,它可以指向某个文件的文件信息区,通过其即可访问到该文件。
笔的使用方式:
FILE* pf
基本使用方式:
FILE* pf= fopen("文件名","使用方式(是读还是写还是别的)");
if (pf == NULL) //防止出现野指针
{
perror(fopen);
return 1;
}
其中 文件名如“text.txt” , 使用方式如下
文件使用方式
读写文件运用到的函数如下:
fputc('a', pf);
得到结果如图
//读取一个字符
int ch = fgetc(pf);
if (ch != EOF)
{
printf("%c\n", ch);
}
//连续每次读取一个字符
//文件中有qwertyu
int ch = fgetc(pf);
printf("%c\n", ch); //q
ch = fgetc(pf);
printf("%c\n", ch); //W
ch = fgetc(pf);
printf("%c\n", ch); //e
ch = fgetc(pf);
printf("%c\n", ch); //r
第一段代码得到结果如图
fputs("hahahaha", pf);
这里需要注意的是,如上面3.中原“text.txt”文件内容是qwertyu,但是我们fputs函数比较霸道,每次写入都会把原来的内容覆盖了。
原文件内容:
使用fputs后的文件内容
原文件内容
我们需要创建一个数组来存放一定长度的数据, 然后将所读取的数据放入arr中。
第二段代码中,通过while循环将读取到“鼠标光标”指向数据末尾,也就是读完了。
//读文件指定长度的数据
char arr[10] = { 0 };
fgets(arr, 5, pf);
printf("%s\n", arr)
//读文件全部数据
char arr[20] = { 0 };
while (fgets(arr, 20, pf) != NULL)
{
printf("%s", arr);
}
第一段代码结果如下 值得一提的是,fgets函数读完的结果一定会留最后一个数据给\0 (也就是读5个指定长度,它只读了4个长度,最后一个长度给\0.)
第二段代码结果
这里的fprintf也就比printf多了一个要连接文件与程序文件之间的“笔”——pf
其格式大致为
fprintf(文件指针名,"占位符",要赋值的变量/结构体变量)
对比一下
printf("占位符",要赋值的变量)
同5类似,其格式大致如下
fscanf(文件指针名,"占位符",&赋值的变量/结构体变量)
对比一下
scanf("占位符",&要赋值的变量)
上述的输入——写和输出——读的函数 不仅可以在文件里生效,也可以在程序中像printf或者scanf那样使用
如6中的打印结构体内容
这里我个人理解为,其与printf的区别就是多了个需要笔的工具,这里的笔用stdout代替。
输出的”笔“是stdout,而输入的”笔“则是stdin
因为提到了fprintf和fscanf,干脆也去学了sprintf和sscanf。
如果说,printf是直接把数据打印在程序上
fprintf是把数据拿了支笔打印在程序上,
那sprintf则是直接把数据从结构体上拿起来,转化成字符串(以字符串打印在字符串数组char上)。
此时的arr每个元素中分别以单个字符的形式存储了结构体的数据,变成一串字符串。
同理,sscanf函数运用便是将一个字符串转化成结构体的数据。
此时的结构体b中就包含了数组arr的数据内容。
基本书写格式
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream )
fwrite(要写的数据, 单个数据的长度, 数据元素的个数, 对应的文件指针)
注意打开文件那块用的是”wb“!!
此时你打开文件,就会看见一堆你根本看不懂,但是是二进制的形式的数据内容。
同理 fread的基本格式
size_t fread( void *buffer, size_t size, size_t count, FILE *stream )
fread(要写的数据, 单个数据的长度, 要读取的数据元素个数, 对应的文件指针)
这时你去打印数组的内容,呈现在程序上的是整型的数据,而不是二进制
假定我们要读这个数
实操分析:
完整代码如下:
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//运用fseek函数读文件中第五个元素的数据
//我们指定初始位置、当前文件指针所处位置、末尾位置三个不同的角度来指定位置同个数据的读取
//从头开始读取向后4个偏移量的位置的数据
fseek(pf,4,SEEK_SET);
//打印字符看看是否读取成功
int ch = fgetc(pf);
fprintf(stdout, "%s\n", ch);
//从当前位置(此时当前文件所处位置在第二元素处)向后读取3个偏移量来读取数据
fseek(pf,3, SEEK_CUR);
ch = fgetc(pf);
fprintf(stdout, "%s\n", ch);
//文件末尾读取向前6个偏移量的位置的数据
fseek(pf,-6, SEEK_END);
ch = fgetc(pf);
fprintf(stdout, "%s\n", ch);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
有时候文件读取多了,读着读着咱就读迷糊了,不知道这个文件指针移动到啥位置了,据初始位置有多远,这个时候咱就可以用这个瞅瞅指针走了多远的距离。
具体用法
long int ftell ( FILE * stream )
如上笔记中的pf作为我们文件指针名
具体使用则: ftell(pf)
此时比如文件指针读到1.中的 t的 位置 ,其离初始位置偏移量为4,则该函数返回值为4。
引用王者花木兰的话:“离家太远会忘记故乡...”,咱文件读多了,又不小心迷糊了,想让文件指针回家从头来,这时候就运用到rewind函数啦~
具体用法
void rewind ( FILE * stream )
如在上述例子中
我们使用则: rewind(pf)
基本使用方式
fclose(pf);
pf = NULL; //防止pf变成野指针
文件读取结束有两种原因: ①文件读取到末尾啦 ②文件读取出现异常错误 这俩种情况分别需要两种不同的函数来判断! ①ferrror函数——判断是否文件读取异常错误而结束; ②feof函数——判断是否文件正常读取到尾而结束;
如下,我们实操书写一个错误来具体分析使用这俩个函数,以此判断是否结束
现在基础都会了,但是能用他来干什么呢?
这里提供一个实操的例子:拷贝文件:test1.txt ——> test2.txt
我们知道数据在内存中是以二进制形式存储的。 对于文件而言:如果不加转换直接输出到外存就是二进制文件; 如果要在外存上以ASCII码形式存储,就需要提前转换最后以ASCII码值形式存储的文件就是文本文件。 我的疑惑:为什么读写文件还有二进制的形式?这个用二进制形式的函数都有什么作用呢? 二进制文件存储相比于文本文件存储而言,有什么更好的地方吗?
几经网上查找和询问,我的疑惑终于得到了解答。
这里我直接引用CSDN的其他大佬的博客的解释
到这里,诸位看官觉得如何?
呜呜我我我应该不会原地胖十斤把QAQ...