tips
1. 打开一个文件 -> 内存里面就会创建一个对应关联的文件信息区 -> 文件信息区其实就是一个FILE类型的结构体 -> 各个结构体成员记录了该文件的种种信息 -> 结构体(文件信息区)由FILE* 结构体指针来维护 -> 有了指针,一切都好说了
2. 使用文件可以让我们的数据持久化存储,存起来之后无论以后程序关掉电脑关机,下一次重新运行起来,文件数据还在。
3. 电脑硬盘上所放的这些文件就被称为文件。
4. 文件指针非常重要,每次打开一个文件,就会在内存中开辟一个与这个文件关联对应的文件信息区,这个文件信息区用来存放文件的相关信息,说白了它也没什么高级的,就是一个结构体变量,类型为FILE(由系统来声明),本质上就是一个结构体,用来描述文件信息的。
5. fopen需要交代文件名与打开方式。如果正确打开,返回一个结构体指针(指向打开文件相关联的文件信息区在内存中的地址,维护文件信息区的),有了指针之后,就可以进行操作。打开失败会返回NULL,可以去判断一下。
6.当要关闭文件的时候,用fclose,关闭后指针置空。
7. scanf与printf是只能针对标准输入输出流的
“流FILE” / “(外部)输出设备” / “维护流的指针FILE*”
1. 什么叫“流”呢?你可以想象成水流。
2. 我们在内存中有数据的时候,这些数据我肯定要输出出来,我们可以打印在屏幕上,我们可以写在文件里面去,也可以放到网络上去,也可以放在硬盘网盘软盘上等其他介质上面,我们把这些叫做输出设备。
未完...................
补充:内存与输入输出/读写的关系
输入输出/读写都是站在内存/程序的角度
1. 我们写的是程序,程序是载入到内存中的,程序的数据都是存放在内存里面的。
2. 如果我们
要将内存中的数据打印在屏幕上,叫:输出/写数据。
要将输入缓冲区(对接键盘)的数据放入内存,叫:输入/读数据。
要将内存的数据放入文件里面,叫:输出/写。
要将文件的数据放入内存中,叫:输入/读。
3. 未来我们的内存中的数据可能会输出到不同的各种各样的输出设备上去。我们内存数据打印在屏幕上,或者放在文件里或者放在网络上等等,这些各自的方式方法肯定都不一样,但是我们写C程序的时候,难道我们给这些外部输出设备(文件啊,甚至打印机啊等等)从内存输出数据的方式我都要一一懂吗?如果各种各样外部输出设备读写方式我都要懂的话,对我来说太复杂了。
4. 这个时候我们就不关心外部输出设备了,C语言在这中间呢抽象出“流”这样一个概念,就像一个蓄水池一样,C语言你呢只要关心我把数据放到这个“流”里面去就可以了,至于说这些内存数据是如何到什么文件啊,屏幕啊,打印机啊,网络啊,各种盘啊什么的,不要关心,由C语言帮你分装,所有的这些内存里面要输出的数据我通通放到流里面去就OK了,这样大大简化了C语言程序员写代码的难度。
5. 这个流的类型就是FILE这样一个结构体。这个流其实就是由我们的FILE*stream指针来管理的
流相当于是一个FILE类型的结构体,然后FILE*相当于是维护流的一个指针
内存(程序就是载入到内存)与外部输出设备的关系
1. 任何一个C语言程序运行的时候,默认会打开三个流:stdin(标准输入), stdout(标准输出), stderr(标准错误)。stdin, stdout, stderr就是三个管理标准输入流,标准输出流,标注错误流的FILE*指针
2. 这个stdin针对的外部设备就是键盘,这个stdout针对的就是屏幕,stderr也是针对的屏幕。这三个管理流的指针的类型都是FILE*(流就可以理解为FILE类型的结构体)
3. 这也是为什么我每次从键盘上读取数据,从屏幕上打印信息,我们什么也不关心,直接无脑printf printf scanf scanf,那么因为已经默认打开了这三个流stdin,stdout stderr(标准流)。
4. 除了这三个C语言本身默认打开的流以外,如果说内存数据要输出到文件里面,你要去看看针对文件的流有没有被打开, 如果说内存数据要输出到网络里面,你要去看看针对网络的流有没有被打开, 如果说内存数据要输出到软盘里面,你要去看看针对软盘的流有没有被打开........
内存(程序就是载入到内存)与文件的关系
1. 但是对于文件这个流来说,C语言是没有默认打开这个流的
2. 我们在操作文件的时候,首先要打开文件拥有这个流
3. 打开文件就好比打开了(没有被C语言默认打开的)针对文件的流,fopen()返回的FILE*指针与维护流的指针是一个道理
4. 打开了这个流就可以对文件输入输出数据了。
5. 这个流与我们打开文件的FILE*文件指针是一个道理的
有了上面有关于流的认识与内存->输出设备(尤其是文件)的关系认识之后........ 我们就来了解一下文件的顺序读写
读写的时候有两大类读写方式:一种是文件的顺序读写;另一种是文件的随机读写,顺序读写都是按照一定的顺序去读写的
要想真正掌握这些函数:只需要抓住一根主线:
字符输出 fputc() 适用于所有输出流 ![](https://i-blog.csdnimg.cn/blog_migrate/3f51cf7fea07af8ff584262b22e415be.png)
1. fputc是一个字符一个字符去写进去文件,可以考虑循环。
2. 并且下一个写进去的字符是跟在前一个写进去的字符的屁股后面,像这样子有顺序的一直往后写,不会这儿写一个,那儿写一个这样子乱写,所以也叫顺序读写。
3.
字符输入 fgetc() 适用于所有输入流
1. 读的时候道理一样,用函数fgetc去读字符,也是一个一个单独地这么去读.
2. fgets返回的是此次读到的那个字符的ASCII值。可以接受一下,用printf%c打印出来。
3. 当文件里面有多个字符的时候,我们用fgetc一个个去读取,此时定位文件位置的指针在fgetc每次读取完一次之后,就会后移一个字节往后走了一步指到下一个字符去了,因此fgetc一个个读取的话不需要顾虑太多,每次读取的时候只需要传参同一个FILE*结构体指针(事实上你其他的也传不了)
(注:以w的形式打开文件,然后写入,文件里面的相关信息会被销毁与覆盖,重新写)
1. fgetc与fputc是针对字符的,读一个字符或者写一个字符。
2. fgetc如果读取成功,那么读取的字符的ASCII码值就会被返回,若读取失败,EOF就会被返回。
3. fputc的返回内容与fgetc雷同。
4. 字符与ASCII码要灵活转化,不要太死板。
文本行(一行字符串)输出 fputs() 适用于所有输出流
1. fputs有两个参数,一个是字符串的首字符的地址,一个是要写进去的文件对应的文件信息区的文件指针。
2. 字符串常量=字符串首字符的地址。
3. 多个fputs使用写字符串进去的时候,如果你想要在互相之间有换行,fputs是不会自己给你弄的,你自己在写入字符串的时候就要自己输入\n换行符。
文本行(一行字符串)输入 fgets() 适用于所有输入流
1. fgets参数有文件指针,还有char* str字符指针(这个就是说把从文件指针指向的文件信息区对应的文件里面读取到的东西拷贝到哪里),还有num(这个就是说在读取文件的时候读取多少个字符)
2. 读取的时候要考虑一下\0,当你写参数说给我读五个字符,而真正发现屏幕上就只有4个字符,是因为它硬要在你的末尾放上一个\0,导致占用了你一个字符。因此,以后你想要读取5个的时候,你就写6就OK了,其他道理一样的。
3. 读取字符串的时候,空格也属于一个字符
4. 注意:这两个函数是只处理一行的,一行处理完了之后我就不会继续去处理下一行的,下一行与我一点关系没有。反正我只处理一行。
5. 并且如果我在读的时候碰着了\n(我们上面的fputs函数能写\n进去的从而导致换行),那么我也就直接换行了。
格式化输出 fprintf() 适用于所有输出流
1. fprintf写入数据的话是带有格式化的数据
2. 对于fprintf与printf是非常类似的,我们去看一下它们的函数原型,发现参数里面后面有... ,这个东西其实比较奇怪的,叫做“可变参数列表"。其实也没有什么高级的,我们去回顾一下已经熟悉的不得了的printf的参数,你发现它的参数可以有1个,也可以有2个,3个,4个.....都可以有。后面的参数设计是为了具象化格式化里的模板。
3. 如果用fprintf的话非常简单,就先正常按照printf去写,然后在括号最前面再加上一个参数,即:指向要写入文件的文件信息区的文件指针。这就是把格式化的信息写到文件里面去。
格式化输入 fscanf() 适用于所有输入流
1. 格式化输出文件有了,那么如何格式化地读取文件呢?用fscanf。
2. fscanf就是从文件里面把里面的信息拿到内存里面去。这个函数又与scanf十分相似,参数比scanf多了一个FILE*指针。
3. 因此你先用scanf一如既往像以前那样正常写好,注意:scanf/fscanf的末尾几个参数都必须是地址形式,因为我是要把数据真真切切地读入到内存里面去,必须给我传地址!当然,数组名是首元素地址就不用&了。再在最前面加上参数:文件指针就OK了。
二进制输出 fwrite() 只适用于针对文件的流
1. 它是只能针对文件这个流的。
2. 当此时在打开文件的时候,这个打开方式可不能就用w与r了,因为那是打开一个文本文件.而我现在要打开一个二进制文件,应该用wb或者rb。
3. fwrite有四个参数,我此时此刻是从内存中往文件里面写入二进制数据,那我这个二进制数据到底是什么类型的呢?有各种各样的类型可能,但无论如何,二进制本身而言它就是死的。
4. 勤用sizeof
5. 我们以二进制的形式把内存中的数据写入到文件里面去,然后我们点开文件,确实里面的内容有些貌似可以看懂,有些是看不懂的。那我就让电脑通过二进制的方式在读出来。
6. 道理一样的,虽然我们肉眼看不懂,那就让程序去读。
二进制输入 fread() 只适用于针对文件的流
顺带着一提:还有两组非常奇葩的函数
现在并不是流指针在与内存数据打交道了,而是内存数据与内存数据在自己打交道了
一个格式化的数据与字符串在打交道,站在格式化数据的立场上