C语言---FILE结构体
一、FILE 结构体的本质与定义
-
基本概念
FILE
是 C 语言标准库中用于封装文件操作的结构体类型,定义于<stdio.h>
中。它代表一个“文件流”,可以是磁盘文件、标准输入输出(stdin/stdout/stderr)或其他输入输出设备。 -
实现特性
- 具体成员由编译器实现决定(如 GCC、Clang、MSVC 可能不同),不可直接访问内部字段,必须通过标准库函数操作。
- 包含文件句柄、缓冲区、状态标志、位置指针等关键信息。
二、FILE 结构体的核心成员(抽象功能描述)
虽然具体成员不透明,但可归纳其核心功能模块:
-
文件标识与连接
- 文件描述符(如 Unix 的
int fd
,Windows 的HANDLE
):底层系统用于标识文件的句柄。 - 打开模式:记录文件以读、写、追加、文本/二进制模式打开的状态(如
r
,w+
,ab
等)。
- 文件描述符(如 Unix 的
-
缓冲区管理
- 缓冲区指针:指向用于暂存数据的内存区域(如
char* buffer
)。 - 缓冲区大小:缓冲区的容量(如
size_t buffer_size
)。 - 当前缓冲区位置:记录已使用的缓冲区长度(如
size_t cur_pos
)。 - 缓冲区类型:全缓冲(默认文件)、行缓冲(stdout)、无缓冲(stderr),可通过
setvbuf
配置。
- 缓冲区指针:指向用于暂存数据的内存区域(如
-
文件位置与偏移
- 位置指针:记录当前读写位置(二进制文件为字节偏移,文本文件可能涉及换行符转换后的逻辑位置)。
long int pos
(或类似成员):通过ftell
/fseek
操作的底层位置。
-
状态标志
- 错误标志(
ferror
):文件操作出错时置位(如磁盘损坏、权限不足)。 - EOF 标志(
feof
):文件读取到末尾时置位。 - 打开状态:标记文件是否已关闭(避免重复关闭导致错误)。
- 错误标志(
-
宽字符与本地化
- 宽字符流(C99 引入):若处理宽字符(如
wchar_t
),包含额外的宽字符缓冲区和转换状态(如FILEW
,C11 合并为FILE
支持宽字符)。
- 宽字符流(C99 引入):若处理宽字符(如
三、文件流的打开与关闭
-
打开文件:
fopen
与模式字符串- 原型:
FILE* fopen(const char* filename, const char* mode);
- 模式说明:
- 基础模式:
r
(读,不存在则失败)、w
(写,清空或创建)、a
(追加,不存在则创建)。 - 二进制模式:追加
b
(如rb
,wb+
),避免文本模式的换行符转换(Windows 下\r\n
↔\n
)。 - 更新模式:追加
+
(如r+
可读可写,不允许同时读写同一位置未刷新)。
- 基础模式:
- 返回值:成功返回
FILE*
,失败返回NULL
(需检查!)。
- 原型:
-
关闭文件:
fclose
- 作用:刷新缓冲区(未写入的数据强制写入磁盘)、释放资源、关闭底层文件句柄。
- 返回值:成功返回
0
,失败返回EOF
(如磁盘已满、文件被删除)。 - 注意:程序结束时自动关闭所有打开的文件流,但显式调用
fclose
是良好习惯。
四、文件读写操作与缓冲区机制
-
字符级操作
- 读:
int fgetc(FILE* stream)
(读单个字符,返回unsigned char
转换为int
,EOF 时返回EOF
)。 - 写:
int fputc(int c, FILE* stream)
(写单个字符,成功返回c
,失败返回EOF
)。
- 读:
-
行/字符串操作
- 读:
char* fgets(char* s, int size, FILE* stream)
(读取一行或size-1
个字符,包含\n
,末尾补\0
)。 - 写:
int fputs(const char* s, FILE* stream)
(写入字符串,不包含末尾\0
)。
- 读:
-
块读写(二进制文件)
- 原型:
size_t fread(void* ptr, size_t size, size_t count, FILE* stream);
size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);
- 作用:按块读取/写入数据,
size*count
为总字节数,返回实际操作的完整块数(可能小于count
因错误或 EOF)。
- 原型:
-
格式化读写
- 读:
int fscanf(FILE* stream, const char* format, ...);
(按格式解析输入,返回成功匹配的参数数)。 - 写:
int fprintf(FILE* stream, const char* format, ...);
(按格式生成输出,返回实际写入的字符数)。
- 读:
-
缓冲区控制
- 自动缓冲:标准库根据流类型自动选择缓冲策略(文件默认全缓冲,终端行缓冲,stderr 无缓冲)。
- 手动配置:
int setvbuf(FILE* stream, char* buffer, int mode, size_t size);
mode
:_IOFBF
(全缓冲)、_IOLBF
(行缓冲)、_IONBF
(无缓冲)。
- 强制刷新:
int fflush(FILE* stream)
(刷新缓冲区,对读流无意义,stream=NULL
时刷新所有输出流)。
五、文件定位与随机访问
-
绝对定位
int fseek(FILE* stream, long offset, int origin);
origin
:SEEK_SET
(文件开头)、SEEK_CUR
(当前位置)、SEEK_END
(文件末尾)。- 文本文件限制:
offset
必须是之前ftell
的返回值(因换行符转换可能导致逻辑与物理位置不一致)。
-
相对定位
void rewind(FILE* stream);
(将位置重置为开头,清除错误和 EOF 标志)。
-
获取当前位置
long ftell(FILE* stream);
(返回当前位置,文本文件可能不精确,需配合fseek
使用)。int fgetpos(FILE* stream, fpos_t* pos);
和int fsetpos(FILE* stream, const fpos_t* pos);
(更精确的定位,支持大文件)。
六、错误处理与状态检查
-
错误标志
int ferror(FILE* stream);
(非零表示有错误,需在操作后立即检查)。void clearerr(FILE* stream);
(清除错误和 EOF 标志)。
-
EOF 检测
int feof(FILE* stream);
(仅在读取操作失败后为真,避免提前判断while(!feof(stream))
导致多读一次)。
七、标准流与特殊文件流
-
预定义的标准流
stdin
(标准输入,对应键盘,默认打开,r
模式)。stdout
(标准输出,对应屏幕,默认打开,w
模式,行缓冲)。stderr
(标准错误,对应屏幕,默认打开,w
模式,无缓冲,错误信息即时输出)。
-
临时文件
FILE* tmpfile(void);
(创建临时二进制文件,关闭或程序结束时自动删除)。char* tmpnam(char* s);
(生成唯一的临时文件名,避免冲突)。
八、高级特性与注意事项
-
二进制 vs 文本模式
- 文本模式:自动转换换行符(如 Windows 下写入
\n
转为\r\n
,读取时反转),可能导致文件大小变化。 - 二进制模式:原样读写字节,适用于图片、可执行文件等,避免换行符干扰。
- 文本模式:自动转换换行符(如 Windows 下写入
-
宽字符流
- C99 引入宽字符函数(如
fgetwc
,fputwc
,fwprintf
),通过fopen
的模式L
(如L"rb"
)打开宽字符流,处理wchar_t
数据。
- C99 引入宽字符函数(如
-
多字节流与本地化
fgetc
/fputc
处理单字节字符,fgetws
/fputws
处理宽字符,依赖本地化环境(setlocale
)。
-
线程安全
- 标准 IO 函数通常是线程安全的,但多个线程同时操作同一
FILE
流可能导致缓冲区竞争(建议加锁或使用独立流)。
- 标准 IO 函数通常是线程安全的,但多个线程同时操作同一
-
常见陷阱
- 未检查
fopen
返回值导致空指针解引用。 - 文本模式下对二进制文件操作导致数据损坏(如
\r
被过滤)。 - 忘记刷新缓冲区(如程序崩溃前未
fflush
或fclose
,导致数据丢失)。 fgets
未指定缓冲区大小导致溢出(必须传入size
参数)。
- 未检查
九、总结
FILE
结构体是 C 语言文件 IO 的核心,通过标准库函数间接操作,涵盖以下核心知识:
- 文件打开与关闭:模式字符串、错误检查、资源释放。
- 读写操作:字符、行、块、格式化,缓冲区机制。
- 定位与状态:位置指针、错误/EOF 标志、缓冲控制。
- 特殊流与高级特性:标准流、临时文件、二进制/文本模式、宽字符支持。
- 最佳实践:错误处理、避免缓冲区溢出、合理使用缓冲策略。