导言
平时我们在写程序时,在运行时申请内存空间,运行完时内存空间被收回,如果想要持久化的保存,我们就可以使用文件,所以下文将要介绍一些在程序中完成一些文件操作。
目录
文件流
我们常使用的是标准流,它们在使用C语言编写程序时已经默认打开:
●stdin—标准输入流,大部分从键盘输入,比如scanf函数
●stdout—标准输出流,大部分从显示器输出,比如printf函数
●stderr—标准错误流,大部分从显示器输出
这三种流,属于FILE*类型的指针,我们常常称为文件指针。
文件指针
文件指针FILE*都指向了一个包含文件信息的结构体。
每个被使用的文件都会自动开辟一个文件信息区,这个文件信息区存放着文件的各种信息,他被存放在一个结构体中,结构体的类型是由系统命名的,命名为FILE。
vs2013这个文件类型声明 struct _iobuf { char *_ptr; int _cnt; char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; }; typedef struct _iobuf FILE;//typedef重命名struct _iobuf为FILE
这样我们在后面使用时创建一个FILE*型的指针,就可以指向任意一个文件信息区(结构体变量)了。
放一张bit课件的图:
文件的打开与关闭
前面我们提到在C语言中默认打开的流只有标准流,那么我们想要完成文件操作时,那么我们就得先打开文件流,在完成操作后再关闭文件流。
打开:
使用函数fopen()打开文件
函数参数及其返回值
FILE* fopen(const char* filename,const char* made); //filename:文件名(文件路径) //made:打开方式 //返回值:FILE*,一个指向filename文件信息区的文件指针,执行失败返回NULL
注意点
●关于参数filename两种表达方式:
绝对路径:从根目录开始创建(C/D盘),任何文件都可访问,如:c:\code\test.txt
在C语言中地址是个字符串,\会被当做转义字符,那么我们可以使用\\或者/的方式来代替
相对路径:以当前路径为基础访问,“.”表示当前路径,“..”表示上一层路径。如: 假设我的当前路径为c:\cyuyan\code\test.txt 我的桌面路径:c:\cyuyan\code\desktop 我想用相对路径访问我的桌面:..\desktop\ 相对路径在本目录访问文件时常常会省略.\,直接使用文件名+后缀的方式访问 ●关于常用参数mode:●为了输入(读)数据, mode为“r”,存在:打开,不存在:报错 ●为了输出(写)数据,并将目标文件内容清空, mode为“w”,存在:打开,不存在:新建一个
使用举例:
文件运行程序前:
运行程序:
文件运行程序后:
关于文件打开值得注意的是:mode的参数决定了后续的文件操作的权限,以读的方式打开只能进行一些读的操作,以写的方式打开只能进行一些写的操作。
关闭:
使用函数fclose()关闭文件
函数参数及其返回值
int fclose(FILE* stream); //stream:文件流 //返回值:成功:0,失败:EOF(-1)
文件打开后不关闭会造成内存泄漏,这与动态内存不free是一个道理,最后记得置NULL。
使用举例:
文件操作
顺序读写:
单个字符:
fputc函数
功能:写单个字符到流。(输出——写)
函数参数及其返回值
int fputc(int character ,FILE* stream); //character:单个字符 //stream:文件流 //返回值:成功:写入字符的ASCII码值,失败:EOF(-1)
使用举例:
当前目录下的test.txt文件内容:
fgetc函数
功能:从流读单个字符。(输入——读)
函数参数及其返回值
int fgettc(FILE* stream); //stream:文件流 //返回值:成功:读取字符的ASCII码值,失败:EOF(-1)
使用举例:
我们先在当前目录创建一个test.txt文件,并写入字符b保存。
运行程序打印出fgetc的返回值(这个返回值就是文件内字符的ASCII码值):
小写字母b的ASCII码值为98,说明我们读取成功。
值得注意的是fgetc和fputc函数只是针对单个字符的,所以文件存在多个字符时,读取时是第一个字符。
字符串:
fputs函数
功能:写字符串到流。(输出——写)
函数参数及其返回值
int fputs(const char* str,FILE* stream); //str:要写入的字符串 //stream:文件流 //返回值:成功:非负整数,失败:EOF(-1),设置errorno
使用举例:
返回0,也是一个非负整数,且大部分编译器都返回0。
test.txt文件的写入情况:
fgets函数
功能:从流中读取字符串。(输入——读)
函数参数及其返回值
char* fgets(const char* str, int num ,FILE* stream); //str:从流中读取的字符放在这里面 //stream:文件流 //num:读取num-1个字符,最后一个放\0, //返回值:成功:字符串str首地址,失败:NULL
注意点:
●遇到换行符停止读取,且这个换行符也会被读取到str中,所以在一些特定场景下,为了输出格式别忘了替换掉换行符\n。
●fgets函数还常常运用于标准输入流,因为它只有读取到\n时才停止,会读取空格,使用时,stream参数为stdin。
使用举例:
创建一个存放着两行数据的test.txt文件:
运行fgets程序:
我们看到它的确读取成功了,打印时没有加\n,打印出来却自动换行了,说明读取了\n,验证了前面的注意点,我们可以看Debug看一下:
值得注意的是fgets还常常用于完成一些标准输入的操作,它会读取空白字符,且它比gets安全。
格式化:
fprintf函数
功能:格式化数据到流。(输出——写)
函数参数及其返回值
int fprintf(FILE* stream , const char* format,……); //stream:文件流 //format:与printf函数中的“字符串”类似,可以放入占位符 //……:可变参数列表,与printf类似 //返回值:成功:写入字符数,失败:EOF(-1)
使用举例:
运行程序后:
fprintf函数与printf函数类似,只是在参数部分多出了文件流,其他参数不变,返回值也是。
fscanf函数
功能:从流中读取格式化数据。(输入——读)
函数参数及其返回值
int fscanf(FILE* stream , const char* format ,……); //stream:文件流 //format:"占位符"与scanf函数第一个参数类似 //……:可变列表(变量名) //返回值:成功:读取个数,失败:EOF(-1)
使用举例:
创建一个test.txt文件,并写入数据:
运行程序:
可以观察到,fscanf函数与scanf函数类似,只是多出文件流参数, 其他与scanf函数类似,且需要取地址&
其他:
sprintf函数
功能:格式化数据到字符串。(输出)
函数参数及其返回值
int sprintf(char* str, const char* format,……); //str:要写入的字符串 //format:与printf函数中的“字符串”类似,可以放入占位符 //……:可变参数列表,与printf类似 //返回值:成功:写入字符数,失败:EOF(-1)
使用举例:
sscanf函数
功能:从字符串格式化数据。(输入)
函数参数及其返回值
int sscanf(char* str, const char* format,……); //str:要读取的字符串 //format:与scanf函数中的“字符串”类似,可以放入占位符 //……:可变参数列表,与scanf类似 //返回值:成功:读取项数,失败:EOF(-1)
使用举例:
这两个函数可以简单理解为字符串与格式化数据的互换函数。
随机读写:
fseek函数
功能:改变光标位置
函数参数及其返回值
int fseek(FILE* stream , long int offset ,int origin) //stream:文件流 //offset:相对于参数origin的偏移量 //origin:三个选项:SEEK_SET:文件指针起始位置、SEEK_CUR:文件指针当前位置、SEEK_END:文件指针结束位置 //返回值:成功:0,失败:非0整数并设置errorno
使用举例:
我们先创建一个文件存放helloworld 18:
运行程序:
ftell函数
功能:返回文件指针相对于起始位置的偏移量
函数参数及其返回值
long int ftell(FILE* stream); //stream:文件流 //返回值:偏移量
使用举例:
rewind函数
功能:使文件指针回到起始位置
函数参数及其返回值
void rewind(FILE* stream); //stream:文件流 //返回值:无
使用举例:
文件结束判定
feof函数
功能:判断文件结束原因(返回值0:遇到错误,非0:遇到文件尾)
函数参数及其返回值
int feof(FILE* stream); //stream:文件流 //返回值:0:遇到错误,非0:遇到文件尾
使用举例:
ferror函数
功能:判断文件错误状态
函数参数及其返回值
int feof(FILE* stream); //stream:文件流 //返回值:非零值(1):遇到错误,0:没错误
这应该是我所有博客中最长的一篇,因为是笔记的原因,然后也不太熟,所以前前后后几天花了6、7小时,最后也没来的及检查一遍,请佬们斧正,封面图是ai生成的。