输入输出

翻译整理自 cppreference - cstdio。

1. 标准流

C 程序启动时自动打开三个标准流:

流 默认指向
stdin 标准输入(键盘)
stdout 标准输出(屏幕)
stderr 标准错误(屏幕,无缓冲)
#include <stdio.h>
fprintf(stdout, "stdout\n");
fprintf(stderr, "stderr\n");

2. printf / scanf

int age;
printf("age: ");
scanf("%d", &age);

double pi;
scanf("%lf", &pi);

char name[32];
scanf("%31s", name);

scanf 必须传地址。%s 不带宽度是缓冲区溢出高危。

scanf 返回值

int n = scanf("%d %d", &a, &b);
if (n != 2) {
    fprintf(stderr, "input invalid\n");
}

3. 文件操作

FILE *fp = fopen("data.txt", "r");
if (!fp) {
    perror("fopen");
    return 1;
}

// 读
int ch;
while ((ch = fgetc(fp)) != EOF) {
    putchar(ch);
}

// 或整行读
char line[256];
while (fgets(line, sizeof(line), fp)) {
    printf("%s", line);
}

fclose(fp);

写文件

FILE *fp = fopen("out.txt", "w");
fprintf(fp, "name=%s\n", "Go");
fputs("another line\n", fp);
fputc('A', fp);
fclose(fp);

模式

模式 含义
r 只读(文件须存在)
w 覆盖写(不存在则创建)
a 追加
r+ 读写
w+ 读写(覆盖)
a+ 读写(追加)
b 二进制(可组合:rb、wb)

4. 二进制 IO

typedef struct { int id; char name[32]; } User;

User u = {1, "Alice"};
FILE *fp = fopen("user.dat", "wb");
fwrite(&u, sizeof(u), 1, fp);
fclose(fp);

User v;
fp = fopen("user.dat", "rb");
fread(&v, sizeof(v), 1, fp);
fclose(fp);

注意:结构体含指针时不能直接 fwrite,因为指针不指向文件数据。

5. 随机访问

fseek(fp, 0L, SEEK_SET);    // 起始
fseek(fp, 0L, SEEK_END);    // 末尾
fseek(fp, 10L, SEEK_CUR);   // 相对当前位置

long pos = ftell(fp);       // 当前位置
rewind(fp);                  // 等价 fseek(fp, 0, SEEK_SET)

获取文件大小:

fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);

6. 缓冲

stdio 默认带缓冲(行缓冲或全缓冲):

setvbuf(fp, NULL, _IONBF, 0);     // 无缓冲
setvbuf(fp, buf, _IOFBF, sizeof(buf)); // 全缓冲
fflush(fp);                       // 刷新输出

fflush 只对输出流有意义,对输入流是未定义。

7. 字符与字符串 IO

int c = getchar();              // 等价 getc(stdin)
putchar('A');                   // 等价 putc('A', stdout)
char *s = fgets(buf, n, stdin); // 安全,限制长度
puts("hello");                  // 输出 + \n

8. 错误处理

FILE *fp = fopen("x.txt", "r");
if (!fp) {
    perror("fopen");                // 输出形如 "fopen: No such file..."
    fprintf(stderr, "errno=%d\n", errno);
}

<errno.h> 提供 errno、strerror:

#include <errno.h>
#include <string.h>
printf("%s\n", strerror(errno));

ferror / feof:

if (ferror(fp)) fprintf(stderr, "io error\n");
if (feof(fp))   printf("end of file\n");

9. 命令行参数

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "usage: %s <file>\n", argv[0]);
        return 1;
    }
    FILE *fp = fopen(argv[1], "r");
    ...
}

10. sscanf / sprintf

char buf[64];
sprintf(buf, "%d-%d", 2026, 9);

int y, m;
sscanf("2026-9", "%d-%d", &y, &m);

更安全的 snprintf:

snprintf(buf, sizeof(buf), "name=%s", name);

小结

C 的 IO 完全围绕 FILE 指针,掌握 fopen/fread/fwrite/scanf/printf 后已能完成绝大多数任务。