一、前言
在之前的内容中,我们已经学习了文件IO范畴的open、close、read、write和lseek函数,也对文件IO与标准IO的核心差异进行了梳理,本文将聚焦于标准IO(C库)体系,深入解析fopen、fclose、fread、fwrite和fseek这几个关键函数的功能、使用场景与技术细节。
二、fopen函数
1、基本定义
在 Linux 系统中,fopen是标准IO库(C 库)中用于打开文件的核心函数。它封装了底层的系统调用(如open),并提供了缓冲机制,简化了文件操作的复杂度。
2、函数原型
fopen函数的原型定义在 <stdio.h> 头文件中,原型如下:
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
3、相关参数
pathname:表示指向字符串的指针,指定要打开的文件路径(可以是相对路径或绝对路径)。 比如/home/usr/ll或者ll。
mode:字符串常量,表示指定文件的打开方式(读写权限、文件创建 / 截断规则等)。Linux 中常用模式如下:
r:只读方式打开,文件必须存在;
r+:可读写,文件必须存在;
rb+:打开二进制文件,可以读写;
rt+:打开文本文件,可读写;
w:只写,文件存在则文件长度清0,文件不存在则建立该文件;
w+:可读写,文件存在则文件长度清0,文件不存在则建立该文件;
a:附加方式打开只写,不存在建立该文件,存在写入的数据加到文件尾,EOF符保留;
a+:附加方式打开可读写,不存在建立该文件,存在写入的数据加到末尾,EOF符不保留;
wb:打开二进制文件,只写;
wb+:打开或建立二进制文件,可读写;
wt+:打开或建立文本文件,可读写;
at+:打开文本文件,可读写,写的数据加在文本末尾;
ab+:打开二进制文件,可读写,写的数据加在文件末尾;
注:Linux 系统中不区分文本模式与二进制模式(两者行为一致),这是因为 Linux 文件内容本身就是字节流,没有类似 Windows 的换行符(\n与\r\n)转换逻辑。模式中 b 的仅为兼容其他系统(如 Windows)而存在。
4、返回值
成功:返回一个非空的 FILE* 指针(文件流对象),后续通过该指针操作文件。
失败:返回 NULL,同时设置全局变量errno标识错误原因(如文件不存在、权限不足等)。
5、与底层open系统调用的区别
区别如下:
|
维度 |
fopen |
open |
|
缓冲机制 |
自带缓冲,减少系统调用次数 |
无缓冲,每次操作直接触发系统调用 |
|
返回值 |
FILE*(文件流指针) |
整数(文件描述符,fd) |
|
操作接口 |
需配合fread/fwrite/fclose等 |
需配合read/write/close等 |
|
跨平台性 |
符合C标准,跨平台(Linux/Windows 等) |
依赖 Linux 系统,不跨平台 |
6、示例
如下代码所示:
#include<stdio.h>
//FILE *fopen(const char *pathname, const char *mode);
int main()
{
FILE*fp=NULL;//初始化fp指针,防止野指针
fp=fopen("file3","w");//创建一个file3文件,并设置只写
if(fp == NULL)//判断文件是否正常打开
{
printf("fopen failed!");
return -1;
}
printf("fopen succeed!");
return 0;
}
接着使用gcc编译器进行编译,然后运行文件:
可以看到文件打开成功。
三、fclose函数
1、基本定义
在 Linux 系统中,fclose是标准 IO库(C 库)中用于关闭文件流的核心函数,它负责完成文件流的资源释放、缓冲区数据刷新等关键操作。
2、函数原型
fclose函数的原型定义在 <stdio.h> 头文件中,原型如下:
#include <stdio.h>
int fclose(FILE *stream);
3、参数与返回值
3.1、参数
stream:类型为FILE*,标识要关闭的目标文件流。
3.2、返回值
成功:返回0;
失败:返回EOF(即 -1),同时设置全局变量errno标识错误原因。
4、与底层close系统调用的区别
区别如下:
| 维度 | fclose(标准 I/O) | close(系统调用) |
|---|---|---|
| 操作对象 | FILE*(文件流指针) | 整数(文件描述符 fd) |
| 功能范围 | 刷新缓冲区 + 释放标准 I/O 资源 + 调用 close | 仅关闭文件描述符,无缓冲刷新逻辑 |
| 错误处理 | 需通过 errno 或返回值 EOF 判断 | 需通过返回值 0 成功,-1失败)和 errno 判断 |
| 跨平台性 | 符合 C 标准,跨平台(Linux/Windows 等) | 依赖 Linux 系统,不跨平台 |
5、示例
接上面fopen那里的例子展开来说,和open、close函数一样,fopen与fclose函数也是密不可分的,如下面代码所示:
#include<stdio.h>
//FILE *fopen(const char *pathname, const char *mode);
//int fclose(FILE *stream);
int main()
{
FILE*fp=NULL;//初始化fp指针,防止野指针
fp=fopen("file3","w");//创建一个file3文件,并设置只写
int ab_fp;
if(fp == NULL)//判断文件是否正常打开
{
printf("fopen failed!\n");
return -1;
}
printf("fopen succeed!\n");
ab_fp=fclose(fp);//关闭文件流
if(ab_fp<0)//判断是否关闭成功
{
printf("fclose failed!\n");
return -2;
}
printf("fclose succeed!\n");
return 0;
}
接着使用gcc编译器进行编译,然后运行文件:

可以看到文件打开成功、成功关闭。
四、fread函数
1、基本定义
在 Linux 系统中,fread是标准 IO库(C库)中用于从文件流读取数据的核心函数,它支持二进制安全的读取操作,可用于文本文件和二进制文件的内容读取。
2、函数原型
fread函数的原型定义在 <stdio.h> 头文件中,原型如下:
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
3、相关参数
如下图所示:
| 参数 | 类型 | 含义 |
|---|---|---|
| ptr | void* | 指向缓冲区的指针,用于存储从文件流中读取的数据。 |
| size | size_t | 每个 “数据元素” 的字节大小(例如:读取 int 类型时,int 为 sizeof(int) )。 |
| nmemb | size_t | 要读取的 “数据元素个数”。 |
| stream | FILE* | 要读取的文件流指针(由fopen等函数返回)。 |
4、返回值
fread返回实际成功读取的数据元素个数(而非字节数)。
(1)返回值等于nmemb:表示成功读取了请求的所有元素;
(2)返回值小于nmemb:可能是遇到了文件末尾(EOF) 或读取错误;
(3)返回值等于0:表示未读取到任何数据;
5、与底层read系统调用的区别
区别如下:
| 维度 | fread(标准 I/O) | read(系统调用) |
|---|---|---|
| 缓冲机制 | 自带缓冲区(减少系统调用次数,提升效率) | 无缓冲,每次调用直接触发系统调用 |
| 返回值 | 实际读取的元素个数(size_t) | 实际读取的字节数(size_t) |
| 跨平台性 | 符合 C 标准,跨平台(Linux/Windows 等) | 依赖 Linux 系统,不跨平台 |
| 适用场景 | 需频繁小数据量读取、或读取结构化数据(如结构体) | 需直接控制底层 I/O、或处理非缓冲场景(如设备文件) |
6、示例
如下代码所示:
#include<stdio.h>
#include<string.h>
//FILE *fopen(const char *pathname, const char *mode);
//int fclose(FILE *stream);
//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
int main()
{
FILE*fp=NULL;//初始化fp指针,防止野指针
fp=fopen("file3","r");//创建一个file3文件,并设置只读
char readbuf[12];//设置一个数组
memset(readbuf,0,sizeof(readbuf));//初始化数组,防止乱码
int ab_fp;
if(fp == NULL)//判断文件是否正常打开
{
printf("fopen failed!");
return -1;
}
printf("fopen succeed!\n");
ab_fp=fread(readbuf,4,2,fp);//将fp文件中的数据读入readbuf这个数组中,数据个数为4*2
if(ab_fp<=0)
{
printf("fread failed!\n");
return -3;
}
printf("read %s\n",readbuf);
ab_fp=fclose(fp);//关闭文件流
if(ab_fp<0)//判断关闭是否成功
{
printf("fclose failed!");
return -2;
}
printf("fclose succeed!\n");
return 0;
}
接着使用gcc编译器进行编译,在创建的文件file3中写入12345678,然后运行文件:
可以看到成功地将 12345678 8个字节读取了出来。
注:如果不初始化数组,会导致乱码。
五、fwrite函数
1、基本定义
在 Linux 系统中,fwrite是标准 IO库(C 库)中用于向文件流写入数据的核心函数,支持二进制安全的写入操作,可用于文本文件和二进制文件的内容写入。
2、函数原型
fwrite函数的原型定义在 <stdio.h> 头文件中,原型如下:
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
3、相关参数
如下图所示:
| 参数 | 类型 | 含义 |
|---|---|---|
| ptr | const void* | 指向待写入数据缓冲区的指针(数据的源头),const 表示数据在写入过程中不会被修改。 |
size | size_t | 每个 “数据元素” 的字节大小(例如:写入int类型时,size为sizeof(int))。 |
nmemb | size_t | 要写入的 “数据元素个数”。 |
stream | FILE* | 要写入的文件流指针(由fopen等函数返回)。 |
4、返回值
fwrite返回实际成功写入的 “数据元素个数”(而非字节数)。
(1)返回值等于nmemb:表示成功写入了请求的所有元素;
(2)返回值小于nmemb:表示写入过程中出现了错误;
(3)返回值等于0:表示未写入任何数据;
5、与底层write系统调用的区别
如下图所示:
| 维度 | fwrite(标准 I/O) | write(系统调用) |
|---|---|---|
| 缓冲机制 | 自带缓冲区(减少系统调用次数,提升效率) | 无缓冲,每次调用直接触发系统调用 |
| 返回值 | 实际写入的元素个数(size_t) | 实际写入的字节数(size_t) |
| 跨平台性 | 符合 C 标准,跨平台(Linux/Windows 等) | 依赖 Linux 系统,不跨平台 |
| 适用场景 | 需频繁小数据量写入、或写入结构化数据(如结构体) | 需直接控制底层 I/O、或处理非缓冲场景(如设备文件) |
6、示例
如下代码所示:
#include<stdio.h>
#include<string.h>
//FILE *fopen(const char *pathname, const char *mode);
//int fclose(FILE *stream);
//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
//size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
int main()
{
FILE*fp=NULL;//初始化fp指针,防止野指针
fp=fopen("file3","r+");//创建一个file3文件,并设置可读可写
char readbuf[12];//设置一个数组
char writebuf[]={"hello world"};//设置一个数组,数组内容为“hello world”
memset(readbuf,0,sizeof(readbuf));//初始化数组,防止乱码
int ab_fp;
if(fp == NULL)//判断文件是否正常打开
{
printf("fopen failed!");
return -1;
}
printf("fopen succeed!\n");
ab_fp=fread(readbuf,4,2,fp);//将fp文件中的数据读入readbuf这个数组中,数据个数为4*2
if(ab_fp<=0)
{
printf("fread failed!\n");
return -3;
}
printf("read %s\n",readbuf);
ab_fp=fwrite(writebuf,4,1,fp);//将writebuf中的数据写入fp文件中,数据个数为4*1
if(ab_fp<=0)
{
printf("fwrite failed\n");
return -4;
}
ab_fp=fclose(fp);//关闭文件流
if(ab_fp<0)//判断关闭是否成功
{
printf("fclose failed!");
return -2;
}
printf("fclose succeed!\n");
return 0;
}
接着使用gcc编译器进行编译,然后使用cat指令来查看file3的内容:
可以看到8后面多了hell这4个字符,说明成功写入(在8后面是因为读取完12345678后光标定在末尾)。
六、fseek函数
1、基本定义
在 Linux 系统中,fseek是标准 IO库(C 库)中用于移动文件流指针的核心函数,它允许程序在文件中随机定位读写位置,是实现文件随机访问的关键工具。
2、函数原型
fseek函数的原型定义在 <stdio.h> 头文件中,原型如下:
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
3、相关参数
如图所示:
| 参数 | 类型 | 含义 |
|---|---|---|
stream | FILE* | 要操作的文件流指针(由fopen等函数返回)。 |
offset | long | 偏移量(带符号整数):正数表示向后偏移,负数表示向前偏移(需确保偏移后位置有效)。 |
whence | int | 定位参考点,取值为以下宏(定义在<stdio.h>中):- SEEK_SET:以文件开头为参考点;- SEEK_CUR:以当前文件指针位置为参考点;- SEEK_END:以文件末尾为参考点。 |
4、返回值
成功:返回0;
失败:返回 EOF(即 -1),同时设置全局变量errno标识错误原因。
5、与底层lseek系统调用的区别
如下图所示:
| 维度 | fseek(标准 I/O) | lseek(系统调用) |
|---|---|---|
| 操作对象 | FILE*(文件流指针) | 整数(文件描述符 fd) |
| 定位粒度 | 基于long类型偏移量(通常为 4/8 字节,取决于系统) | 基于off_t类型偏移量(支持更大范围,如 64 位系统下的大文件) |
| 跨平台性 | 符合 C 标准,跨平台(Linux/Windows 等) | 依赖 Linux 系统,不跨平台 |
| 缓冲影响 | 会同步文件流的缓冲区状态(确保指针位置与缓冲数据一致) | 仅操作底层文件描述符的指针,不影响标准 I/O 的缓冲区 |
6、示例
如下代码所示:
#include<stdio.h>
#include<string.h>
//FILE *fopen(const char *pathname, const char *mode);
//int fclose(FILE *stream);
//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
//size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
//int fseek(FILE *stream, long offset, int whence);
int main()
{
FILE*fp=NULL;//初始化fp指针,防止野指针
fp=fopen("file3","r+");//创建一个file3文件,并设置可读可写
char readbuf[12];//设置一个数组
char writebuf[]={"hello world"};//设置一个数组,数组内容为“hello world”
memset(readbuf,0,sizeof(readbuf));//初始化数组,防止乱码
int ab_fp;
if(fp == NULL)//判断文件是否正常打开
{
printf("fopen failed!");
return -1;
}
printf("fopen succeed!\n");
ab_fp=fread(readbuf,4,2,fp);//将fp文件中的数据读入readbuf这个数组中,数据个数为4*2
if(ab_fp<=0)
{
printf("fread failed!\n");
return -3;
}
printf("read %s\n",readbuf);
ab_fp=fseek(fp,-4,SEEK_CUR);//在当前位置向前偏移4个字节
if(ab_fp<0)//判断是否偏移成功
{
printf("fseek failed\n");
return -4;
}
ab_fp=fwrite(writebuf,4,1,fp);//将writebuf中的数据写入fp文件中,数据个数为4*1
if(ab_fp<=0)
{
printf("fwrite failed\n");
return -5;
}
ab_fp=fclose(fp);//关闭文件流
if(ab_fp<0)//判断关闭是否成功
{
printf("fclose failed!");
return -2;
}
printf("fclose succeed!\n");
return 0;
}
接着使用gcc编译器进行编译,然后使用cat指令来查看file3的内容:
可以看到hell这4个字符向前移动了4个字节(需要先提前将原来创建的文件file3先删掉,不然最后结果会是1234hellhell)。
1126

被折叠的 条评论
为什么被折叠?



