1. 文件缓冲区刷新 (fflush)
1.1 问题背景
- 在使用标准 IO 进行格式化输出到文件时,有时会出现以下情况:
- 调用写函数(如
fprintf)显示成功。 - 立即使用
cat命令查看文件,发现内容为空。
- 调用写函数(如
- 原因:标准 IO 是全缓存(Full Buffered)的。
- 在没有调用
fclose或缓冲区未满时,数据写入的是缓冲区(Buffer),而非直接写入磁盘文件。 - 只有当缓冲区刷新或文件关闭时,数据才会真正写入文件。
- 在没有调用
1.2 解决方案:fflush
- 功能:在不关闭文件的情况下,强制将缓冲区的内容刷新写入到文件中。
- 优势:刷新后仍可继续对该文件流进行操作,无需重新
fopen。 - 函数原型:
int fflush(FILE *stream); - 参数:
stream:指定要刷新的文件流指针(例如fp)。
- 用法示例:
fprintf(fp, "Hello"); fflush(fp); // 立即将 "Hello" 写入文件 // 后续可继续操作 fp
2. 文件流定位 (fseek, ftell, rewind)
2.1 定位的必要性
- 文件流内部维护了一个读写指针(File Position Indicator)。
- 写操作后,指针会移动到文件末尾。若此时直接读取,无法读到刚才写入的内容。
- 需要重新定位指针到文件开头或特定位置才能继续读取。
- 对比:
- 文件 IO (File Descriptor) 使用
lseek。 - 标准 IO (File Stream) 使用
fseek。
- 文件 IO (File Descriptor) 使用
2.2 fseek 函数详解
- 功能:移动文件流的读写指针位置。
- 函数原型:
int fseek(FILE *stream, long offset, int whence); - 参数说明:
stream:文件流指针。offset:偏移量(Offset)。- 正数:向后偏移。
- 负数:向前偏移。
- 零:位置不变。
whence:基准位置(宏定义)。SEEK_SET:文件开头。SEEK_CUR:当前位置。SEEK_END:文件末尾。
- 注意事项:
- 若文件以追加模式(
a或a+)打开,fseek对写操作无效(写入始终强制追加到末尾)。 - 标准 IO 缓冲区大小有限(通常为 4096 字节),适用于一般文本文件操作。
- 若文件以追加模式(
2.3 ftell 函数
- 功能:获取当前文件流指针相对于文件开头的偏移量。
- 函数原型:
long ftell(FILE *stream); - 返回值:
long类型,表示当前的字节位置。 - 用途:
- 获取当前读写位置。
- 配合
fseek获取文件大小。
2.4 rewind 函数
- 功能:将文件流指针直接重置到文件开头。
- 等价操作:
rewind(fp); // 等价于 fseek(fp, 0, SEEK_SET); - 特点:用法简单,专门用于回到文件开头。
3. 代码演示与指针移动逻辑
3.1 实验步骤
-
打开文件:使用
w+模式(读写模式),不可使用a+(追加模式会影响定位)。FILE *fp = fopen("test.txt", "w+"); -
初始位置检查:
- 调用
ftell(fp),返回值为0(文件开头)。
- 调用
-
写入数据:
- 使用
fwrite写入 10 个字符(例如 "HelloWorld")。
char buf[] = "HelloWorld"; fwrite(buf, sizeof(char), 10, fp); - 使用
-
写入后位置检查:
- 调用
ftell(fp),返回值变为10(指针移至第 10 个字节后)。
- 调用
-
重置指针:
- 使用
fseek(fp, 0, SEEK_SET)或rewind(fp)回到开头。 - 再次
ftell确认位置为0。
- 使用
-
读取数据:
- 使用
fread读取 5 个字符。 - 读取后指针移动 5 个字节,
ftell返回5。
- 使用
-
相对偏移演示:
- 从当前位置向前回退 5 个字节:
fseek(fp, -5, SEEK_CUR);- 此时
ftell返回0。 - 若回退 8 个字节(假设当前在 10),
ftell返回2。
4. 实战练习:获取文件大小
4.1 实现思路
利用 fseek 和 ftell 配合,无需遍历读取文件即可获取大小。
- 打开文件。
- 使用
fseek将指针定位到文件末尾 (SEEK_END)。 - 使用
ftell获取当前指针位置,该值即为文件总字节数。 - 关闭文件。
4.2 代码示例
#include <stdio.h>
void get_file_size(const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
printf("Open file failed\n");
return;
}
// 1. 定位到文件末尾,偏移量为 0
fseek(fp, 0, SEEK_END);
// 2. 获取当前位置(即文件大小)
long file_size = ftell(fp);
// 3. 输出结果
printf("File size: %ld bytes\n", file_size);
fclose(fp);
}
4.3 关键点总结
- 返回值类型:
ftell返回long类型,定义变量时需使用long。 - 宏定义头文件:
SEEK_SET,SEEK_CUR,SEEK_END通常包含在<stdio.h>中。 - 效率:该方法比使用
fread循环读取统计字节数更高效。 - 验证:对于 1800 字节的文件,该方法返回值应为 1800。
5. 常见问题与注意事项
- 模式选择:进行读写定位测试时,务必使用
w+或r+,避免使用a+,因为追加模式会忽略写操作的定位。 - 指针状态:每次读写操作后,文件流指针都会自动移动,需清楚当前指针位置以便后续操作。
- 缓冲区影响:若写入后未刷新或未关闭,直接查看文件可能看不到内容,调试时注意调用
fflush。 - 二进制与文本:在 Windows 环境下处理二进制文件时,建议打开模式加上
b(如rb,wb),防止换行符转换影响字节计算。