目录
1. 为什么使用文件
我们前面介绍结构体时,写了通讯录的程序,当通讯录运行起来的时候,可以给通讯录中增加、删除数据,此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了,等下次运行通讯录程序的时候,数据又得重新录入
我们在想既然是通讯录就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才删除。这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。
2. 什么是文件
磁盘上的文件是文件。但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
2.1 程序文件
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
2.2 数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
下面也会介绍到的。
2.3 文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用。文件名包含3 部分:文件路径 + 文件名主干 + 文件后缀例如: c:\code\test.txt为了方便起见,文件标识常被称为 文件名 。
3. 文件的打开和关闭
3.1 文件指针
缓冲文件系统中,关键的概念是 “ 文件类型指针 ” ,简称 “ 文件指针 ” 。每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。注意FILE是一个结构体。
每当打开一个文件的时候,系统会根据文件的情况自动创建一个 FILE结构的变量,并填充其中的信息。一般都是通过一个 FILE 的指针来维护这个FILE 结构的变量,这样使用起来更加方便。
FILE * pf ; // 文件指针变量
定义 pf 是一个指向 FILE 类型数据的指针变量。可以使 pf 指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联 的文件 。
3.2 文件的打开和关闭
文件在读写之前应该先 打开文件 ,在使用结束之后应该关闭文件。在编写程序的时候,在打开文件的同时,都会返回一个 FILE* 的指针变量指向该文件,也相当于建立了指针和文件的关系。
// 打开文件FILE * fopen ( const char * filename , const char * mode );// 关闭文件int fclose ( FILE * stream);当fopen函数打开文件失败会返回一个空指针这里这样写会返回一个空指针的,因为这个文件还没有创建,以"r"的形式打开没有创建的文件就会返回空指针,下面也列举了文件的打开方式。
打开方式:文件使用方式就是mode如果这里我们以"w"的形式打开文件,就会在源文件下创建一个新的文件,可以看出大小为0字节。
当我们放进abcdef的时候,这时候大小就会增加,但是当们再次运行代码后,这个文件大小又会变成0。
所以以"w"的形式打开,文件中内容就会被销毁。
关闭文件:这里和free这个函数很像,就是我们打开了文件最后一定要关闭。
4. 文件的顺序读写
当我们要写文件的时候是输出操作,相当于:源文件向文件输出读文件的时候是输入操作,相当于:文件向源文件输入也就是说源文件是主体,从源文件输出,和从其他地方向源文件输入。这里挺不好理解的(我觉得)。fputc和fgetc函数
这里运行成功也没有打印所以就不放出来了
运行成功就会把26个字母放到文件中
既然把26个字母放到了文件中,那么可不可以把内容拿出来打印到我们的屏幕上呢?
fgetc函数返回值是int,也就是说成功返回字符的ASCII码值,如果错误或者访问到文件末尾就会返回EOF。
这两个函数是针对字符操作的。
fputs和fgets函数
这一对函数和上面两个函数不同的地方就是最后一个字母换成了"s","s"是操作一行的意思。
fputs向文件中输入两行字母,后面加了"\n"就可以换行,如果不加就会放置在同一行。
既然文件里有了内容,继续把它打印出来。fgets函数第一个参数是把文件拿出来后存放的位置;第二个参数是存放的最大个数;第三个参数为FILE指针。
fgets也是一行一行的执行,调用两次就可以拿出两行。还有一点要注意的就是n最大拿出的个数是n-1个,留下的那一个给'\0'。
当然现在我们知道是两行,想要打印多行还是得这样写,因为fgets函数如果失败或者读取到最后一行会返回一个空指针,相当于0,也是假。
fprintf和fscanf函数
这两个是格式化输入输出函数,要想对一个结构体操作,就要选择这两个函数
这两个函数第一个参数就是FILE指针,第二个参数和printf,scanf使用方式一样。也就是前面加了一个参数。
下一步就是拿出来打印,还要重新创建一个结构体变量,把拿出来的数据放到这里面,最后再打印出来。
也可以使用fprintf,要注意的就是第一个参数要改成stdout,也就是打印到屏幕上。
fwrite和fread函数
fwrite函数第一个参数为想要输出的数据的地址,第二个参数为想要输出的字节,第三个参数为想要输出几个,第四个参数为FILE指针。
虽然打开文本之后除了名字我们看得懂其他的都看不懂,是因为这个名字是以文本的形式输出的,其他的数据是以二进制输出的。
fread函数的参数和fwrite函数的参数一样,只不过是void* buffer用不用const修饰的区别。还是重新定义一个新的结构体变量s1来存放输入的数据。
4.1 对比一组函数
scanf/fscanf/sscanfprintf/fprintf/sprintf
5. 文件的随机读写
5.1 fseek
5.2 ftell
返回文件指针相对于起始位置的偏移量
5.3 rewind
当我们输出了很多内容的时候,如果我们想要返回起始位置,可能会使用上面的fseek来定位,传入SEEK_SET记录文件起始位置,偏移量设置成0。那这个函数就可以直接返回文件的起始位置。
6. 文本文件和二进制文件
根据数据的组织形式,数据文件被称为 文本文件 或者 二进制文件 。数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是 二进制文件 。如果要求在外存上以 ASCII 码的形式存储,则需要在存储前转换。以 ASCII 字符的形式存储的文件就是 文 本文件 。
当我们打开这个源文件所在的obj文件,也就是二进制文件,以记事本的形式打开就可以看到这是一堆乱码,所以用文本文件打开二进制文件就会这样。
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
如有整数 10000 ,如果以 ASCII 码的形式输出到磁盘,则磁盘中占用 5 个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4 个字节
当我们使用编译器以二进制的形式打开这个文本就会如下。
如何以二进制打开呢,这里也说一下。
7. 文件读取结束的判定
在文件读取过程中,不能用 feof 函数的返回值直接用来判断文件的是否结束。而是 应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束所以feof函数判断的是原因,而不是结果。
8. 文件缓冲区
ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
下面就写一个代码来探究一下。
结束代码关闭文件。
这里可以得出一个 结论 :因为有缓冲区的存在, C 语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做,可能导致读写文件的问题。
结语:
那写到这,文件操作的内容就介绍到这里。