news 2026/9/30 2:38:13

C语言:(15.文件操作)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言:(15.文件操作)

一、为什么需要文件?

程序运行时的数据都在内存中,程序结束就消失了。文件可以让数据持久化保存。

// 内存中的结构体变量 struct student { char name[20]; int age; double score; long long ID; }; // 文件 = 保存在磁盘上的结构体数据 // 可以保存文字、视频、音乐等

本质:文件就是内存中的结构体变量,申请很多字节空间,保存想要保存的信息。

二、FILE结构体

C语言中,文件用FILE结构体表示:

struct _iobuf { char* _ptr; int _cnt; char* _base; int _flag; int _file; int _charbuf; int _bufsiz; char* _tmpfname; }; typedef struct _iobuf FILE;

FILE本质上是struct _iobuf的别名,通过typedef定义。

三、文件的路径

3.1 绝对路径 vs 相对路径
类型示例说明
绝对路径D:\12345\FILE\FILE_type.txt从盘符开始的完整路径
相对路径.\FILE\FILE_type.txt相对于当前程序的路径

生活中的比喻:

绝对路径:中国 陕西省 XX市 XX区 XX大学XX校区 相对路径:XX大学XX校区
3.2 路径的转义字符
// 错误:\ 是转义字符 FILE* p = fopen("D:\12345\FILE\FILE_type.txt", "r"); // 正确:用 \\ 表示 \ FILE* p = fopen("D:\\12345\\FILE\\FILE_type.txt", "r"); // 也可以用 / FILE* p = fopen("D:/12345/FILE/FILE_type.txt", "r");

四、fopen:打开文件

FILE* fopen(const char* filename, const char* mode);

参数:

  • filename:文件路径(相对/绝对)

  • mode:操作权限

返回值:

  • 成功:返回文件结构体指针

  • 失败:返回NULL(文件不存在或路径错误)

4.1 常用mode
mode含义文件存在文件不存在
"r"只读打开成功返回NULL
"w"只写清空内容创建新文件
"a"追加在末尾追加创建新文件
"r+"读写打开成功返回NULL
"w+"读写清空内容创建新文件
"a+"读写追加末尾追加创建新文件
4.2 打开文件示例
#include <stdio.h> int main() { // 相对路径 FILE* p = fopen(".\\FILE\\FILE_type.txt", "r"); // 绝对路径 // FILE* p = fopen("D:\\12345\\FILE\\FILE_type.txt", "r"); if (p == NULL) { printf("文件不存在\n"); return 0; } printf("文件存在,可以操作\n"); fclose(p); return 0; }

五、fclose:关闭文件

int fclose(FILE* stream);

功能:关闭文件,释放资源。

重要:每次fopen后必须fclose,否则会造成资源泄漏!

FILE* p = fopen("test.txt", "r"); if (p == NULL) return -1; // 使用文件... fclose(p); // 必须关闭

六、fgetc/fputc:单字符读写

6.1 fgetc:读取一个字符
int fgetc(FILE* stream);

返回值:

  • 成功:返回该字符的ASCII码(int类型)

  • 读到文件末尾:返回EOF(-1)

FILE* p = fopen("test.txt", "r"); if (p == NULL) return -1; char ch; while ((ch = fgetc(p)) != EOF) { printf("%c", ch); } fclose(p);
6.2 fputc:写入一个字符
int fputc(int c, FILE* stream);

返回值:

  • 成功:返回写入字符的ASCII码

  • 失败:返回EOF

FILE* p = fopen("test.txt", "w"); if (p == NULL) return -1; char arr[] = "春眠不觉晓\n处处闻啼鸟\n"; for (int i = 0; arr[i] != '\0'; i++) { fputc(arr[i], p); } fclose(p);

七、fgets/fputs:字符串读写

7.1 fgets:读取一行
char* fgets(char* str, int size, FILE* stream);

特性:

  • 遇到\n或读到size-1个字符停止

  • \n会存入缓冲区,自动追加\0

  • 最多读取size-1个字符(预留1字节存\0)

返回值:

  • 成功:返回缓冲区首地址

  • 读到末尾/失败:返回NULL

FILE* p = fopen("test.txt", "r"); if (p == NULL) return -1; char buffer[128]; while (fgets(buffer, sizeof(buffer), p) != NULL) { printf("%s", buffer); } fclose(p);
7.2 fputs:写入字符串
int fputs(const char* str, FILE* stream);

特性:

  • 遇到\0停止

  • 不会自动添加换行符

  • 需要换行手动加\n

FILE* p = fopen("test.txt", "w"); if (p == NULL) return -1; fputs("庆历四年春,\n", p); fputs("滕子京谪守巴陵郡。\n", p); fclose(p);

八、fscanf/fprintf:格式化读写

8.1 fprintf:格式化写入
int fprintf(FILE* stream, const char* format, ...);

对比:

printf("序号:%d\n", i); // 输出到控制台 fprintf(p, "序号:%d\n", i); // 输出到文件
8.2 fscanf:格式化读取
int fscanf(FILE* stream, const char* format, ...);

对比:

scanf("%d", &num); // 从控制台读取 fscanf(p, "%d", &num); // 从文件读取
8.3 完整示例
// 写入 void FILE_Fprintf() { FILE* p = fopen("fmt.txt", "w"); if (p == NULL) { printf("文件打开失败\n"); return; } for (int i = 1; i <= 3; i++) { fprintf(p, "序号:%d 文本测试\n", i); } fclose(p); } // 读取 void FILE_Fscanf() { FILE* p = fopen("fmt.txt", "r"); if (p == NULL) { printf("文件打开失败\n"); return; } int num; while (fscanf(p, "序号:%d 文本测试\n", &num) != EOF) { printf("读取到序号 = %d\n", num); } fclose(p); } int main() { FILE_Fprintf(); // 先写入 FILE_Fscanf(); // 后读取 return 0; }

九、rewind:重置文件指针

void rewind(FILE* stream);

功能:将文件指针重新定位到文件开头。

FILE* p = fopen("test.txt", "w+"); if (p == NULL) return -1; // 写入 fputs("庆历四年春,\n滕子京谪守巴陵郡。\n", p); // 指针已经移动到末尾,需要rewind才能从头读取 rewind(p); // 读取 char buffer[128]; while (fgets(buffer, sizeof(buffer), p) != NULL) { printf("%s", buffer); } fclose(p);

十、综合实战:从文件读取学生信息

#include <stdio.h> #include <stdlib.h> typedef struct student { char name[20]; int age; double score; char sex[5]; } student; student tulun2510[30]; int len = 0; // 从文件读取 void FILE_ReadWrite_scanf() { FILE* p = fopen("D:\\12345\\FILE\\FILE_type_3.txt", "r"); if (p == NULL) { printf("文件创建失败\n"); return; } printf("文件存在\n"); while (1) { if (feof(p)) break; // 文件结束判断放在最前面 fscanf(p, "%s %d %lf %s\n", tulun2510[len].name, &tulun2510[len].age, &tulun2510[len].score, tulun2510[len].sex); len++; } // 输出 for (int i = 0; i < len; i++) { printf("%s %d %lf %s\n", tulun2510[i].name, tulun2510[i].age, tulun2510[i].score, tulun2510[i].sex); } fclose(p); } // 写入文件 void FILE_ReadWrite_printf() { FILE* p = fopen("D:\\12345\\FILE\\FILE_type_4.txt", "w"); if (p == NULL) { printf("文件创建失败\n"); return; } printf("文件存在\n"); for (int j = 0; j < len; j++) { fprintf(p, "%s %d %lf %s\n", tulun2510[j].name, tulun2510[j].age, tulun2510[j].score, tulun2510[j].sex); } fclose(p); } int main() { FILE_ReadWrite_scanf(); FILE_ReadWrite_printf(); return 0; }

注意:feof(p)的判断位置很重要!放在fscanf后面会污染最后一行导致少输出一行。

十一、面试考点与避坑指南

考点1:fopen的返回值必须检查
FILE* p = fopen("不存在的文件.txt", "r"); if (p == NULL) { // 必须检查 printf("文件打开失败\n"); return -1; }
考点2:fclose的重要性
// 忘记fclose会导致: // 1. 文件句柄泄漏 // 2. 缓冲区数据可能未写入磁盘 // 3. 其他程序无法访问该文件 FILE* p = fopen("test.txt", "w"); // ... 操作 ... // 忘记 fclose(p);
考点3:EOF的本质
// EOF是常量,值为-1 // fgetc返回int而不是char,就是为了能区分EOF和有效字符 char ch; // 错误:char可能无法表示EOF int ch; // 正确:用int接收 while ((ch = fgetc(p)) != EOF) { putchar(ch); }
考点4:fgets和fputs的区别
函数操作换行符
fgets读字符串保留\n
fputs写字符串不自动加\n
考点5:rewind的必要性
FILE* p = fopen("test.txt", "w+"); fputs("hello", p); // 指针移到末尾 // 此时直接读取会读不到任何东西 rewind(p); // 必须重置指针 char buf[100]; fgets(buf, 100, p); // 现在可以读取到"hello"

十二、函数速查表

函数功能返回值
fopen打开文件FILE* / NULL
fclose关闭文件0 / EOF
fgetc读1个字符ASCII / EOF
fputc写1个字符ASCII / EOF
fgets读一行缓冲区地址 / NULL
fputs写字符串非负值 / EOF
fscanf格式化读成功个数 / EOF
fprintf格式化写字符数 / 负值
rewind重置指针无
feof判断文件结束非0 / 0

十三、高级总结

知识点要点
文件本质保存在磁盘上的结构体数据
FILE结构体typedef struct _iobuf FILE
路径绝对路径 vs 相对路径,\\表示\
打开模式r/w/a/r+/w+/a+
关闭文件必须fclose,否则资源泄漏
单字符读写fgetc/fputc
字符串读写fgets/fputs
格式化读写fscanf/fprintf
重置指针rewind

十四、课后作业

  1. 实现一个函数,统计文件中某个字符出现的次数

  2. 实现一个函数,将文件中的所有小写字母转换为大写

  3. 实现一个函数,将两个文件的内容合并到第三个文件

  4. 用fprintf/fscanf实现学生信息的存取

  5. 以下代码有什么问题?

    char ch; while ((ch = fgetc(p)) != EOF) { putchar(ch); }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 2:37:41

Kopia 备份方案:去重加密增量备份的开源实现

Kopia 备份实战&#xff1a;内容寻址去重为什么让增量备份几乎零成本 备份工具的竞争中&#xff0c;Kopia&#xff08;Apache-2.0&#xff09;靠三件事立足&#xff1a;内容寻址去重、端到端 AES-256 加密、S3/B2 等云存储原生支持。本文从备份工具的"该有的样子"讲…

作者头像 李华
网站建设 2026/9/30 2:37:26

【BlueZ 】内核态 L2CAP 基础:与 BlueZ 用户态的数据包交互流程

L2CAP(Logical Link Control and Adaptation Protocol)是蓝牙协议栈的数据链路层,负责将上层应用数据分割为蓝牙基带包传输,并提供协议复用、组寻址、流量控制等核心能力。Linux 内核实现了完整的 L2CAP 协议栈,BlueZ 用户态通过 L2CAP Socket 与内核交互。本文基于 BlueZ…

作者头像 李华
网站建设 2026/9/30 2:36:03

丙烷-二氧化碳复叠制冷系统

丙烷-二氧化碳复叠制冷系统&#xff08;R290-R744&#xff09;。该系统相比传统的氟利昂&#xff08;HFC&#xff09;制冷系统&#xff0c;相当于减少了7000吨二氧化碳温室气体的排放 该系统以丙烷&#xff08;R290&#xff09;为高压级制冷剂&#xff0c;以二氧化碳&#xff0…

作者头像 李华
网站建设 2026/9/30 2:36:02

npm命令解析

npm i -D unplugin-auto-importnpm i: npm install 的简写&#xff0c;用于安装依赖包 -D: --save-dev 的简写&#xff0c;装到开发依赖&#xff08;devDependencies&#xff09; unplugin-auto-import: 包名&#xff0c;就是 Vite 里那个自动导入插件为什么用 -D 而不是直接 n…

作者头像 李华