欢迎来到博主的专栏:C语言进阶指南
流(scream)是一个抽象出来的概念,表示一个输入的源,或者输出的目的地。
比如前面常常用的scanf函数,就是从输入流(键盘输入的字符)向程序输入数据。printf函数就是通过输出流,向屏幕输出数据。
对文件进行读/写操作时,也需要打开一个流,通过这个流来输入/输出数据
打开电脑中的存储信息,这些存储在硬盘的被称为文件。文件有程序文件,数据文件
程序文件,用来存储程序(.exe)或者程序的源码(比如.c文件)的文件
数据文件,存储用来读/写的数据的文件称为数据文件
文件操作函数对象通常是数据文件,因此本篇文章中文件指的通常是数据文件
数据文件根据数据存储的形式又分为二进制文件和文本文件
将数据以二进制的形式存储的文件被称为二进制文件。
比如将一个int类型的数据10000以二进制的形式存储在文件当中,那么数据在二进制中被记录为
考虑到vs编译器数据在内存中的存储形式是小端存储,所以实际的二进制存储形式应为
16 27 00 00(16进制)
数据以ASCII码的形式存储在文件当中,这种文件被称为文本文件
以整型数据10000为例,文本文件的存储形式为数据的ASCII码形式。
在日常当中,计算机与外部设备是经常出现数据传递的,比如键鼠对计算机的操作、计算机向音箱传递数字信号等,这些都是计算机与外部设备的数据交换。
通常来说,完成这些操作都需要有硬件设备来进行连接,这些接口往往要进行硬件层面的操作,来实现这些功能。以图为例
作为更擅长于软件操作的程序员来说,程序员更需要考虑的是这些数据的处理、不用过多的考虑怎么将这些数据输出\输入到硬件设施。
流(scream)就能很好的做到这一点,流是一个承载这些数据的抽象概念。⽅便程序员对各种设备进⾏⽅便的操作。如果需要对某些数据进行输入\输出的操作,只需要对“流”进行操作。
至于“流”与外部设备如何连接、传输,这是更加底层的原理、交由负责这一领域的工作者完成。程序员要做到的是对流进行操作。
“流”可以视为数据与外部设备的数据中转站,因为“流”是一大堆数据的抽象,可以视为是一片数据构成的数据地带。
文件里的数据可以通过流,传输给计算机。也可以让计算机通过流,传输给文件。 因此文件和计算机之间的流称为“文件流”。
先来观看一个文件指针是如何创建的
FILE* PF;
可以看到这个指针的类型是很陌生的,不同于以往常见的数据类型(比如int,char,float……)。
这个FILE类型的变量不是C语言中原有的变量,而是一个定义在
每个文件在打开时都会在内存中开辟一个”文件信息区”。在这个信息区中,存储的内容都是文件的信息、如文件名、文件状态、文件位置等。
而这些数据的类型则是FILE类型,FILE类型可以在头文件中看到定义(不同的编译器有着不同的定义)
比如vs中的FILE类型的定义为
可以看到一个文件的信息、全都保存在这个结构体类型的数据当中。
用来指向这片文件信息区的指针,就叫做文件指针,类型为FILE*。
首先观察fopen的函数原型的第一个参数
filename是一个char*类型的指针,可以认为是一个字符串,这个字符串的含义是想要打开的文件“流”的文件名
文件名有两种形式
(1)相对路径
,即这个文件在磁盘中,相对于想要使用文件“流”的程序文件的相对的目录。
比如一个程序文件“test.c”为例,要在这个文件中打开一个“data.txt”的文本文件。可以翻找磁盘中“data.txt”的路径,相对路径的文件名遵循以下规则(“test.c”为当前文件,“data.txt”为目的文件)
.\表示当前文件的目录。
. .\表示当前文件的上一级目录。
如果目的文件在当前文件的上一级目录下再上一级,就在开头多加上. .\,以此类推。(注意\之前的小数点!!!!)
如果目的文件和当前文件一个目录,那么可以省略“.\”。
以下图为例。
“data.txt”与“test.c”在同一目录下,于是“data.txt”与“test.c”的相对路径为“.\data.txt”或者“data.txt”。
注意这个相对路径要以字符串的形式写进去,为了避免“\”生成转义字符,所以正确的写法应为
FILE* pf=fopen("data.txt", );
或者
FILE* pf=fopen(".\\data.txt",);
如果“data.txt”在“test,c”的上一级目录。
那么写法应为
FILE* pf=fopen("..\\.\\data.txt", "w");
可以看到"data.txt"与“test.c”的相对路径是上一个目录的。
(2)绝对路径
可以通过查看文件的属性找到该文件的位置,然后复制粘贴下文件的所有级别的目录。
比如“data.txt”的绝对路径则是D:\code\test-c\test_1.4\data.txt(不知道为什么我的复制粘贴只能到当前目录–+,没事手动加上)
要注意避免老问题,即转义字符的问题。那么代码应写成
FILE* pf=fopen("D:\\code\\test-c\\test_1.4\\data.txt",);
绝对路径的弊端在于,当程序移植到其他平台时,由于不同计算机的目录命名不同,移植性会大打折扣(通俗来说就是又要加班改代码啦!!!)所以建议使用相对路径来编写文件操作的
“mode”是一个char*类型的指针,可以当做一个字符串 “mode”是特定的字符串,用来决定文件的打开形式
以“data.txt”为例,如果想要以“只读”的形式打开,那么mode就是“r”。
FILE* pf=fopen("data.txt", "r");
不同的打开模式,决定了程序员对该文件的操作权限,比如“只读”模式下,就只能从“data.txt”文件中读入数据,而不能向该文件写入数据。
fopen函数在输入了正确的参数时,会在文件信息区创建出该文件的文件信息,并返回一个FILE类型的指针来指向这片区域,所以通常用一个FILE类型的指针来接收该函数的返回值
FILE* pf=fopen("data.txt", "w");
若文件打开失败,则函数的返回值为NULL。所以可以对指针pf进行检测。
FILE* pf=fopen("data.txt", "w");
if (pf == NULL)
{
exit(EXIT_FAILURE);
}
此时pf可以当做是这个文件与计算机之间的“流”了,后续对“流”的操作,都是使用文件对应的文件指针来操作。
文件流的关闭则显得比较简单了。
参数是一个file*类型的指针,这个指针指向先前打开的文件流
比如关闭“data.txt”的文件“流”,将pf作为参数传输进去即可
FILE* pf=fopen("data.txt", "w");
if (pf == NULL)
{
exit(EXIT_FAILURE);
}
fclose(pf);
文件关闭函数顺利关闭文件流,则返回值为0。
如果文件关闭函数发生错误,返回值为EOF。
所以可以对fclose函数进行检测,来判断文件流关闭是否存在异常。