C语言文件操作核心指南:打开关闭、读写与缓冲机制详解
C语言通过标准IO库实现对硬盘数据的持久化存储。核心流程包括:使用FILE*指针管理文件状态,通过fopen指定模式(读/写/追加等)打开文件,利用fread/fwrite或fprintf/fscanf进行顺序读写,借助fseek实现随机访问,并在结束前务必fclose以刷新缓冲区并释放资源。同时需区分文本与二进制文件,并正确使用EOF与feof判断读取状态。
1. 文件标识与文件名构成
在C语言中,每个文件都必须拥有唯一的标识以便用户引用。文件名由三部分构成:文件路径 + 文件名主干 + 文件后缀。例如 c:\demo\test.txt 中,c:\demo\ 为路径,test 为主干,.txt 为后缀。
2. 文件指针与 FILE 结构体
当文件被打开时,系统会在内存中创建一个名为 FILE 的结构体变量,用于存放文件名、当前读写位置、缓冲区信息等关键数据。开发者通常无需关心 FILE 内部的具体字段,而是通过一个指向该结构的指针变量来进行操作。
定义文件指针变量的语法如下:
FILE *pf;
pf 即为文件信息区的索引,通过它即可访问关联的文件内容。
2.1 文件的打开与关闭
文件在使用前必须打开,使用后必须关闭,以释放资源和保存数据。
- 打开函数:
FILE * fopen ( const char * filename, const char * mode ); - 关闭函数:
int fclose ( FILE * stream );
常见的打开模式包括 r (只读)、w (写入,若存在则清空)、a (追加)、b (二进制模式)等。以下是一个完整的打开-读取-关闭示例:
// 仅展示核心逻辑
FILE* pf = fopen("test.txt", "r");
if (pf == NULL) {
// 建议打印错误信息以便调试
printf("%s\n", strerror(errno));
return -1;
}
// ... 执行文件操作 ...
fclose(pf);
pf = NULL; // 避免悬垂指针
3. 文件的顺序读写
顺序读写是从文件开头到结尾依次进行。根据数据类型和存储格式的不同,主要有以下几种方式。
3.1 字符读写(文本文件)
适用于单个字符的读写。例如实现简单的文件拷贝:
int main() {
FILE* pr = fopen("data.txt", "r");
if (!pr) return -1;
FILE* pw = fopen("data2.txt", "w");
if (!pw) { fclose(pr); return -1; }
int ch;
while ((ch = fgetc(pr)) != EOF) {
fputc(ch, pw);
}
fclose(pr);
fclose(pw);
return 0;
}
3.2 二进制块读写
直接处理内存中的数据块,不经过ASCII转换,效率更高且能保留原始字节结构。常用函数为 fread 和 fwrite。
// 写入二进制结构体数组
struct Stu s[2] = { {"ZhangSan", 20, 95.5}, {"LiSi", 18, 88.0} };
FILE* pf = fopen("data.bin", "wb");
fwrite(s, sizeof(struct Stu), 2, pf);
fclose(pf);
// 读取二进制结构体数组
struct Stu buffer[2] = {0};
FILE* pr = fopen("data.bin", "rb");
fread(buffer, sizeof(struct Stu), 2, pr);
printf("Name: %s Age: %d Score: %.1f\n", buffer[0].name, buffer[0].age, buffer[0].score);
fclose(pr);
3.3 格式化输入输出
类似于 printf/scanf,但针对文件流。例如读取包含姓名、年龄的结构体数据:
struct Stu {
char name[20];
int age;
double score;
};
// 读取示例
FILE* pf = fopen("data.txt", "r");
struct Stu s = {0};
fscanf(pf, "%s %d %lf", s.name, &s.age, &s.score);
fclose(pf);
4. 文件的随机读写
不需要按顺序从头读到尾,可以通过偏移量直接定位到文件的任意位置。
4.1 fseek:定位偏移
原型:int fseek ( FILE * stream, long int offset, int origin );
origin 通常为 SEEK_SET (文件头)、SEEK_CUR (当前位置) 或 SEEK_END (文件尾)。
FILE* pFile;
pFile = fopen("example.txt", "wb");
fputs("This is an apple.", pFile);
fseek(pFile, 9, SEEK_SET); // 定位到第9个字符后
fputs(" sam", pFile); // 覆盖后续内容
fclose(pFile);
4.2 ftell:获取偏移量
返回当前文件指针相对于文件起始位置的字节偏移量。
long int pos = ftell(pf);
4.3 rewind:复位指针
将文件指针重新定位到文件开头,等效于 fseek(pf, 0, SEEK_SET) 并清除错误标志。
5. 文本文件与二进制文件
区别在于数据在磁盘上的存储形式:
- 文本文件:数据以 ASCII 码形式存储。例如整数
20000会存为 '2','0','0','0','0' 五个字节。 - 二进制文件:数据以二进制补码形式原样存储。整数
20000仅占 4 个字节。
在使用 fopen 时,加上 b (如 rb, wb) 即进入二进制模式。在 Windows 系统中,Windows 换行符是 \r\n,而 Linux/Mac 是 \n。如果不加 b,系统在读写时会进行自动转换,可能导致二进制数据损坏,因此传输二进制数据时必须使用二进制模式。
6. 正确判断文件读取结束
这是一个常见误区:feof() 不能用来做循环终止条件,它仅在读取操作结束后,判断“是因为遇到文件尾还是因为出错”而结束。
6.1 文本文件判断
检查返回值是否为 EOF (fgetc) 或 NULL (fgets)。
int c;
while ((c = fgetc(fp)) != EOF) {
putchar(c);
}
// 循环结束后判断原因
if (ferror(fp)) puts("Read Error");
else if (feof(fp)) puts("End of File reached");
6.2 二进制文件判断
检查实际读取的数量是否小于请求数量。
size_t items_read = fread(buffer, size, count, fp);
if (items_read < count) {
if (feof(fp)) /* 读到末尾 */;
else /* 发生错误 */;
}
7. 文件缓冲区与 I/O 优化
C 语言的标准 I/O 采用缓冲文件系统。数据写入磁盘并非即时完成,而是先存入内存中的缓冲区,装满后才一次性刷入磁盘。这大大减少了磁盘 IO 次数,提升了性能。
7.1 缓冲区刷新机制
如果你希望数据立刻写入磁盘,可以使用 fflush(FILE*) 强制刷新缓冲区。另外,调用 fclose() 时会自动刷新缓冲区再关闭。
FILE* pf = fopen("test.txt", "w");
fputs("Hello", pf);
// 此时文件可能还未写入磁盘
fflush(pf); // 强制刷盘
fclose(pf);
7.2 缓冲类型
不同来源的流具有不同的默认缓冲策略:
- 全缓冲 (Full Buffer):通常在文件上启用,缓冲区满才写入磁盘。
- 行缓冲 (Line Buffer):通常在标准输入输出 (
stdin,stdout) 上启用,遇到换行符或缓冲区满时刷新。例如在终端输入命令,输完回车才会执行。 - 无缓冲 (Unbuffered):如标准错误输出 (
stderr),数据直接立即写出,常用于实时日志记录。
综上,在进行 C 语言文件编程时,务必注意打开/关闭配对、二进制模式的正确选择,以及 fclose 对缓冲区刷新的关键作用。
以上就是C语言文件操作的详细内容,更多关于C语言文件读写技巧的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









