#include <stdio.h>
#include <stdlib.h>
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("data.txt"); /* 打印 "data.txt: <errno描述>",自动带上原因 */
exit(EXIT_FAILURE); /* 立即终止;EXIT_SUCCESS 表示成功 */
}perror 把「你给的前缀 + 全局 errno 对应的系统错误描述」一行打出来;exit 直接结束进程并刷新所有打开的流。错误处理的原则:每个可能失败的调用都检查,失败就带上下文报告——「函数名+文件名+errno」三件套让排错事半功倍。
流(stream)是对「数据源/目的地」的抽象:文件、终端、管道在程序眼里都是 FILE*。打开时选模式:
| 模式 | 含义 | 文件不存在 | 已存在 |
|---|---|---|---|
| "r" | 只读 | 失败 NULL | 从头读 |
| "w" | 只写 | 新建 | 清空! |
| "a" | 追加 | 新建 | 从尾写 |
| "r+" | 读+写 | 失败 NULL | 从头 |
| "w+b" 等加 b | 二进制模式 | — | 不换行翻译 |
程序启动自带三个流:stdin(标准输入)、stdout(标准输出)、stderr(标准错误),不用 fopen 就能用。stderr 不缓冲,错误信息优先于 stdout 显示——报告错误请用它。
| 家族 | 读 | 写 | 撤销 |
|---|---|---|---|
| 字符 | fgetc(fp) / getc / getchar | fputc(ch, fp) / putchar | ungetc(ch, fp) |
| 行 | fgets(buf, n, fp) / gets(禁用!无边界) | fputs(line, fp) / puts | — |
/* 字符级复制:EOF 是「流结束」信号,类型必须是 int! */
int ch;
while ((ch = fgetc(src)) != EOF)
fputc(ch, dst);
/* 行级读取的安全姿势:fgets 自带截断 + 保留换行 */
char line[256];
while (fgets(line, sizeof line, fp) != NULL) {
line[strcspn(line, "\n")] = '\0'; /* 去掉末尾换行(若有) */
process(line);
}| 转换符 | printf 输出 | scanf 读取 |
|---|---|---|
| %d / %u | 有符号 / 无符号十进制 | int* / unsigned* |
| %x / %o | 十六进制 / 八进制 | 同上 |
| %c / %s | 字符 / 字符串 | char*(s 要留 \0 空间!) |
| %f %e %g | 浮点三种风格 | float*;double 用 %lf |
| %ld %lld %zu | long / long long / size_t | 对应指针类型 |
| %p %% | 指针地址 / 百分号本身 | void** / — |
printf 修饰符:%5d(宽度5右对齐)、%-5d(左对齐)、%05d(补零)、%.2f(两位小数)、%*d(宽度由参数给)——第 1 章程序里的 %.*s(精度取自参数的字符串截取)就是这套体系。
scanf 三要害:① 参数必须是指针(&n,字符串数组名本来就退化成指针不用 &);② %s 停在空白且不限长(用 %63s 指定域宽防溢出);③ 返回成功匹配的项目数,用它判断输入是否合法:
int n;
if (scanf("%d", &n) != 1) { /* 输入 abc 时返回 0 */
fprintf(stderr, "不是数字\n");
exit(EXIT_FAILURE);
}struct Rec { int id; double score; };
struct Rec r = { 7, 93.5 };
FILE *fp = fopen("rec.bin", "wb"); /* 二进制模式 */
fwrite(&r, sizeof r, 1, fp); /* 整块写入 */
fclose(fp);
fp = fopen("rec.bin", "rb");
fseek(fp, 1 * sizeof(struct Rec), SEEK_SET); /* 跳到第 2 条记录 */
fread(&r, sizeof r, 1, fp); /* 随机读取 */
long pos = ftell(fp); /* 当前偏移 */
rewind(fp); /* 回到开头 */
fclose(fp);文本 vs 二进制的取舍:文本可读、可移植、易调试,但要转换且占空间;二进制紧凑、免转换、可随机访问,但受字节序/结构填充影响,跨平台要小心。结构体数组、帧数据用二进制整块读写最快。