目录
一、关于文件
1、文件的分类
2、文件名
二、文件相关知识
1、文件指针
2、文件的打开/关闭
"r"示范样例
"w"示范样例
3、文件的顺序读写
相关函数:
fgetc
fputc
fgets
fputs
fscanf
fprintf
fread
fwrite
sprintf
sscanf
4、文件的随机读取
fseek
ftell
rewind
5、文本文件和二进制文件
6、文件读取结束判定
使用文件可以做到将数据直接存放到电脑的硬盘上,做到了数据的持久化
而文件又分为程序文件和数据文件
程序文件:源程序文件(后缀为.c/.cpp)目标文件(后缀为.obj)可执行程序(后缀为.exe)
数据文件:文件的内容是程序读写的数据,例如程序运行需要从中读取数据的文件,或是输出内容的文件
一个文件的文件名包含三部分:
文件路径 + 文件名主干 + 文件后缀
例如:D:\vs2019code\test.c
其中不同的颜色就代表不同的部分
每个被使用的文件都会在内存中开辟一个相应的文件信息区,用于存放文件的相关信息(名称、状态、存储位置....)
这些信息是保存在一个结构体变量中的,该结构体变量是有系统声明的,取名为FILE
当我们打开一个文件时,系统都会根据文件情况自动创建一个FILE结构的变量,填充信息
所以我们可以创建文件指针变量为FILE*
如果定义FILE* pf,则pf是一个指向FILE类型的指针变量,从而可以使pf指向某个文件的文件信息区,进而通过文件信息区的信息可以访问该文件
也就是说通过文件指针变量能够找到与它关联的文件
我们在使用前应打开文件,使用完毕后应关闭文件
打开文件是用fopen
fopen的返回值是FILE*
返回值是一个指针,但是如果遇到一个错误,则会返回空指针
fopen的第一个参数filename,指需要打开的文件名(这里注意:如果与当前的源文件不在一个路径下,也可以写入创建的文件路径)
fopen的第二个参数mode,指打开文件的方式
其中mode有以下几种方式:
"r":只读文件,如果文件不存在,则出错
"w":只写文件,如果文件不存在,则建立一个新文件,如果给的文件已经存在,则该文件会被销毁
"a":追加文件,如果文件不存在,则建立一个新文件
"rb":二进制只读,如果文件不存在,则出错
"wb":二进制只写,如果文件不存在,则建立一个新文件
"ab":二进制追加,如果文件不存在,则出错
关闭文件是用fclose
fclose的参数就是一个FILE*的指针,所以关闭文件时直接传入文件指针即可
我们创建了一个testfile.c的文件,在testfile.c的这个路径下是没有testfile.txt的文件的
这时我们读一个testfile.txt文件:
这时观察结果:
告诉我们没有testfile.txt这个文件
而如果我们在testfile.c路径下创建一个testfile.txt文件:
这时再运行,就会发现不报错了:
上面示范了"r"的时候没有文件会报错,接下来试一下"w"的时候没有文件的情况:
这时依旧没有testfile.txt
代码加运行结果:
可以发现并没有报错,这时再看刚刚的文件夹:
可以发现自动创建了一个testfile.txt的文件,所以我们可以知道"w"时,如果文件不存在,会直接创建文件
上面是"w"时,文件不存在时,会自动创建出文件,那么文件存在时的情况如下:
我们原本就有testfile.txt,里面有内容为2023:
这时再运行代码:
发现文件testfile.txt里面没有内容了,可以明白"w"时,如果给的文件已经存在,则该文件会被销毁
首先这里需要注意输入输出函数的概念:
输入是指从文件里放到程序里面,即读文件
输出指程序里的内容写到文件里面去,即写文件
fgetc:字符输入函数
fgetc返回的是ascll码值,如果读取时遇到错误,则返回EOF
现在我们testfile.txt中有abcde
我们读文件,将他们打印出来:
当然fgetc也可以从键盘读取,stdin表示键盘
fputc:字符输出函数
需要写到文件里的字符是第一个参数,文件指针是第二个参数
运行代码之前,testfile.txt中无内容
用法如下:
这时我们的testfile.txt的内容就是abc了:
fputc也可以打印到屏幕上(stdout)
fgets:文本行输入函数
参数的意义是:将文件指针stream的内容存储到str中,num表示最大存储的个数(规定最多读num-1个)
结果为:
fgets的返回值:正确时返回str,失败时会返回空指针NULL
fputs:文本行输出函数
第一个参数传入一个字符串,第二参数传入一个文件指针
并且连续的使用fputs会加到已经存在的字符串后面
这时testfile.txt中就有以下内容:
fscanf:格式化输入函数
fscanf与fprintf一样,都是先和scanf的用法一样,然后在第一个参数的位置添加一个文件指针
我们的testfile.txt的内容如下:
现在想读文件:
注意我们代码中的结构体变量a,里面什么都没有,我们的内容是从testfile.txt中读取的,结果为:
fprintf:格式化输出函数
看fprintf的参数后面的很复杂,其实不用管后面的参数是什么,我们平常会使用printf,只需要先像使用printf一样,然后在前面加上第一个参数:文件指针即可
下面以结构体类型示例:
testfile.txt现在是空的,运行代码后,testfile.txt内容如下:
fread:二进制输入函数
fread的四个参数与fwrite基本没有区别,具体看下面fwrite的参数含义
是从文件指针里面读取,放到ptr中去
fwrite:二进制输出函数
fwrite的这几个参数意义:
第一个参数ptr,表示指针指向的要写入的数据
第二个参数size,表示每个元素的大小
第三个参数count,表示最多要写入元素的数量
第四个参数是传入一个文件指针
下面是使用示例:
运行后testfile.txt的内容如下:
因为是二进制写入,而记事本是以文本的方式读取的,所以我们不认识
下面用二进制的方式读文件:
结构体变量a本身什么数据都没有,都是从文件中读取的
下面再拓展两个函数sprintf和sscanf,用以理解printf、fprintf、sprintf,scanf、fscanf,sscanf这六个函数的区别:
sprintf是将一个格式化的数据转化成字符串
sprintf与printf的区别就是多了第一个参数,所以用法和printf一样,只需要多第一个参数str,str表示将转换的字符串存储的位置
有一个字符数组arr,初始值为0,有一个结构体变量a,现在将结构体变量a中的格式化的数据,转换为字符串存进arr,然后打印arr,发现就是我们所转化的结构体变量a的内容
sscanf是把一个字符串转化成格式化的数据
sscanf与scanf的区别就是多了第一个参数,即需要提取的字符串位置
通过上面的图片可以看出来,打印出来了两次数据,第一次是用sprintf,将结构体数据转化为字符串,存储到arr中打印出来的
第二次是使用sscanf将arr中的字符串提取出来,以结构体变量b格式化的形式打印(结构体变量b刚开始是没有数据的,是由arr提取出来的)
根据文件指针的位置和偏移量来定位文件指针
第一个参数是文件指针
第二个参数是偏移量(大于0就是向后偏移,小于0就是向前偏移)
第三个参数是起始的位置
第三个参数origin的取值有三个,分别是SEEK_CUR(文件指针当前的位置)、SEEK_END(文件末尾的位置)、SEEK_SET(文件开始的位置)
当前的testfile.txt中内容是abcdef
下面用代码演示随机读文件的用法:
testfile.txt中是abcdef,我们在fseek函数中传入的第三个参数为SEEK_SET,表示文件开始的位置,而文件最开始的位置就是指向a的,偏移量为2,所以就向右偏移两位,即到了c,所以这时用fgetc得到的就是c
随机写文件的用法示例:
先在文件里写入abcd,这时文件指针当前指向d位置的下一个位置,这时使用fseek,第三个参数传入SEEK_CUR就是表示当前位置的指针,偏移量是-2,就是向左移两位,移一位指针指向d,移两位指针指向c,这时在这个位置fputc写入字符'o'
所以testfile.txt中内容就从"abcd"变为了"abod":
ftell可以返回当前指针距离起始位置的偏移量
ftell用法很简单,传入文件指针即可
这里接着上面的fseek的代码演示,最后fputc字符o后,内容为"abod",这时指针会向后移一位,即指向d,这时相对于起始位置的偏移量就是3了,打印看结果:
rewind函数是让文件指针回到文件的起始位置
rewind也是传入文件指针即可使用
我们接着上面的代码演示,刚刚ftell打印出来,偏移量相对于起始位置是3,这时我们调用rewind函数,然后再使用ftell打印偏移量,如下:
数据文件分为文本文件和二进制文件
文本文件就是我们能够看得懂的文件,二进制文件就是我们看不懂内容的意思
数据在内存中是以二进制的形式存储的
如果不转换,直接输出到外存,就是二进制文件
如果以ascll码的形式存储到外存,就是文本文件
feof函数的返回值不能直接判断文件是否结束
而是应用于文件读取结束的时候,是读取失败结束还是遇到文件尾结束的
文本文件读取结束
fgetc判断返回值是否为EOF
fgets判断返回值是否为NULL
二进制文件读取结束
fread判断返回值是否小于实际要读的个数