文件操作<C语言>

导言

平时我们在写程序时,在运行时申请内存空间,运行完时内存空间被收回,如果想要持久化的保存,我们就可以使用文件,所以下文将要介绍一些在程序中完成一些文件操作。


目录

导言

文件流

文件指针

文件的打开与关闭

打开:

关闭:

文件操作

顺序读写:

单个字符:

fputc函数

fgetc函数

字符串:

fputs函数

fgets函数

格式化:

fprintf函数

fscanf函数

其他:

sprintf函数

sscanf函数

随机读写:

fseek函数

ftell函数

rewind函数

文件结束判定

feof函数

ferror函数


文件流

我们常使用的是标准流,它们在使用C语言编写程序时已经默认打开:

●stdin---标准输入流,大部分从键盘输入,比如scanf函数

●stdout---标准输出流,大部分从显示器输出,比如printf函数

●stderr---标准错误流,大部分从显示器输出

这三种流,属于FILE*类型的指针,我们常常称为文件指针。


文件指针

文件指针FILE*都指向了一个包含文件信息的结构体。

每个被使用的文件都会自动开辟一个文件信息区 ,这个文件信息区存放着文件的各种信息,他被存放在一个结构体中,结构体的类型是由系统命名的,命名为FILE。

cpp 复制代码
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()打开文件

函数参数及其返回值

cpp 复制代码
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()关闭文件

函数参数及其返回值

cpp 复制代码
int fclose(FILE* stream);
//stream:文件流
//返回值:成功:0,失败:EOF(-1)

文件打开后不关闭会造成内存泄漏,这与动态内存不free是一个道理,最后记得置NULL。

使用举例:


文件操作

顺序读写:
单个字符:
fputc函数

功能:写单个字符到流。(输出------写)

函数参数及其返回值

cpp 复制代码
int fputc(int character ,FILE* stream);
//character:单个字符
//stream:文件流
//返回值:成功:写入字符的ASCII码值,失败:EOF(-1)

使用举例:

当前目录下的test.txt文件内容:


fgetc函数

功能:从流读单个字符。(输入------读)

函数参数及其返回值

cpp 复制代码
int fgettc(FILE* stream);
//stream:文件流
//返回值:成功:读取字符的ASCII码值,失败:EOF(-1)

使用举例:

我们先在当前目录创建一个test.txt文件,并写入字符b保存。

运行程序打印出fgetc的返回值(这个返回值就是文件内字符的ASCII码值):

小写字母b的ASCII码值为98,说明我们读取成功。

值得注意的是fgetc和fputc函数只是针对单个字符的,所以文件存在多个字符时,读取时是第一个字符。


字符串:
fputs函数

功能:写字符串到流。(输出------写)

函数参数及其返回值

cpp 复制代码
int fputs(const char* str,FILE* stream);
//str:要写入的字符串
//stream:文件流
//返回值:成功:非负整数,失败:EOF(-1),设置errorno

使用举例:

返回0,也是一个非负整数,且大部分编译器都返回0。

test.txt文件的写入情况:


fgets函数

功能:从流中读取字符串。(输入------读)

函数参数及其返回值

cpp 复制代码
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函数

功能:格式化数据到流。(输出------写)

函数参数及其返回值

cpp 复制代码
int fprintf(FILE* stream , const char* format,......);
//stream:文件流
//format:与printf函数中的"字符串"类似,可以放入占位符
//......:可变参数列表,与printf类似
//返回值:成功:写入字符数,失败:EOF(-1)

使用举例:

运行程序后:

fprintf函数与printf函数类似,只是在参数部分多出了文件流,其他参数不变,返回值也是。


fscanf函数

功能:从流中读取格式化数据。(输入------读)

函数参数及其返回值

cpp 复制代码
int fscanf(FILE* stream , const char* format ,......);
//stream:文件流
//format:"占位符"与scanf函数第一个参数类似
//......:可变列表(变量名)
//返回值:成功:读取个数,失败:EOF(-1)

使用举例:

创建一个test.txt文件,并写入数据:

运行程序:

可以观察到,fscanf函数与scanf函数类似,只是多出文件流参数, 其他与scanf函数类似,且需要取地址&


其他:
sprintf函数

功能:格式化数据到字符串。(输出)

函数参数及其返回值

cpp 复制代码
int sprintf(char* str, const char* format,......);
//str:要写入的字符串
//format:与printf函数中的"字符串"类似,可以放入占位符
//......:可变参数列表,与printf类似
//返回值:成功:写入字符数,失败:EOF(-1)

使用举例:


sscanf函数

功能:从字符串格式化数据。(输入)

函数参数及其返回值

cpp 复制代码
int sscanf(char* str, const char* format,......);
//str:要读取的字符串
//format:与scanf函数中的"字符串"类似,可以放入占位符
//......:可变参数列表,与scanf类似
//返回值:成功:读取项数,失败:EOF(-1)

使用举例:

这两个函数可以简单理解为字符串与格式化数据的互换函数。


随机读写:

fseek函数

功能:改变光标位置

函数参数及其返回值

cpp 复制代码
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 函数

功能:返回文件指针相对于起始位置的偏移量

函数参数及其返回值

cpp 复制代码
long int ftell(FILE* stream);
//stream:文件流
//返回值:偏移量

使用举例:


rewind 函数

功能:使文件指针回到起始位置

函数参数及其返回值

cpp 复制代码
void rewind(FILE* stream);
//stream:文件流
//返回值:无

使用举例:


文件结束判定

feof函数

功能:判断文件结束原因(返回值0:遇到错误,非0:遇到文件尾)

函数参数及其返回值

cpp 复制代码
int feof(FILE* stream);
//stream:文件流
//返回值:0:遇到错误,非0:遇到文件尾

使用举例:


ferror函数

功能:判断文件错误状态

函数参数及其返回值

cpp 复制代码
int feof(FILE* stream);
//stream:文件流
//返回值:非零值(1):遇到错误,0:没错误

这应该是我所有博客中最长的一篇,因为是笔记的原因,然后也不太熟,所以前前后后几天花了6、7小时,最后也没来的及检查一遍,请佬们斧正,封面图是ai生成的。

相关推荐
MavenTalk2 分钟前
Move开发语言在区块链的开发与应用
开发语言·python·rust·区块链·solidity·move
simple_ssn9 分钟前
【C语言刷力扣】1502.判断能否形成等差数列
c语言·算法·leetcode
ahadee30 分钟前
蓝桥杯每日真题 - 第10天
c语言·vscode·算法·蓝桥杯
XiaoLeisj34 分钟前
【JavaEE初阶 — 多线程】生产消费模型 & 阻塞队列
java·开发语言·java-ee
2401_8401922737 分钟前
python基础大杂烩
linux·开发语言·python
@东辰41 分钟前
【golang-技巧】- 定时任务 - cron
开发语言·golang·cron
机器人天才一号43 分钟前
C#从入门到放弃
开发语言·c#
好想有猫猫1 小时前
【51单片机】LCD1602液晶显示屏
c语言·单片机·嵌入式硬件·51单片机·1024程序员节
军训猫猫头1 小时前
35.矩阵格式的一到一百数字 C语言
c语言·算法
Mr_Xuhhh2 小时前
递归搜索与回溯算法
c语言·开发语言·c++·算法·github