🎯 本章你会学到:把输入输出从屏幕扩展到文件和命令行,并完成一个统计小项目。建议边读边敲,每节练习先自己做,再展开答案对照。
程序真正的用武之地是处理文件和数据。这一章把前面所有内容串起来:函数、数组、指针、结构体,最后落到读写文件与命令行参数。
命令行参数
把命令行参数当数字相加
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) { /* argc 是参数个数,argv 是参数字符串数组 */
long long sum = 0;
for (int i = 1; i < argc; ++i) { /* argv[0] 是程序名,从 1 开始 */
sum += strtoll(argv[i], NULL, 10);
}
printf("argc=%d sum=%lld\n", argc, sum);
return 0;
}运行结果
argc=4 sum=6
运行示例
./add 1 2 3 # 输出 argc=4 sum=6
读写文件
写文件再读回来
#include <stdio.h>
int main(void) {
const char *path = "numbers.txt";
FILE *out = fopen(path, "w"); /* "w" 写模式,会清空原文件 */
if (out == NULL) {
return 1;
}
for (int i = 1; i <= 3; ++i) {
fprintf(out, "%d\n", i * 10);
}
fclose(out); /* 必须关,否则内容可能还没落盘 */
FILE *in = fopen(path, "r");
if (in == NULL) {
return 1;
}
int value = 0;
long long sum = 0;
while (fscanf(in, "%d", &value) == 1) { /* 读到读不动为止 */
sum += value;
}
fclose(in);
printf("sum=%lld\n", sum);
return 0;
}运行结果
sum=60
运行后输出 sum=60(10+20+30)。文件的打开模式:r 只读、w 覆盖写、a 追加、rb/wb 二进制。
| 函数 | 用途 | 注意 |
|---|---|---|
| fopen / fclose | 打开 / 关闭文件 | 打开失败返回 NULL,一定要判断 |
| fprintf / fscanf | 格式化读写 | 用法跟 printf/scanf 一样,多一个 FILE* |
| fgets | 读一行 | 保留换行符,适合逐行处理文本 |
| fread / fwrite | 二进制读写 | 需要给出元素大小与个数 |
| feof | 是否到文件末尾 | 要在读失败之后判断,不要用它当循环条件 |
⚠️ 易错点
fopen 之后忘了 fclose:写文件时数据可能还在缓冲区,程序非正常结束时内容会丢。⚠️ 易错点 不要用
while (!feof(f)) 当循环条件——它会在读到末尾后再多循环一次,导致最后一条数据被重复处理。用 fscanf(...) == 1 或 fgets(...) != NULL 作条件。✍️ 本节练习
8.1必做统计行数与单词数
从标准输入读到文件结束,统计总行数和单词数(单词以空格、制表符或换行分隔)。
输入 若干行文字,读完会自动结束(Windows 下 Ctrl+Z 回车)。
输出 两行:lines=行数、words=单词数。
样例输入
hello world
foo样例输出
lines=2
words=3💡 提示 用 getchar() 一个字符一个字符读,遇到空白就把 in_word 置 0。
✅ 查看参考答案与解析
#include <stdio.h>
int main(void) {
int lines = 0, words = 0;
int c = 0, in_word = 0;
while ((c = getchar()) != EOF) {
if (c == '\n') {
++lines;
}
if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
in_word = 0;
} else if (!in_word) {
++words;
in_word = 1;
}
}
printf("lines=%d\n", lines);
printf("words=%d\n", words);
return 0;
}解析 getchar 返回 int 而不是 char:这样才能同时容纳 0-255 的字符和 EOF(-1)。这是 C 里一个经典细节。
8.2挑战成绩汇总(综合练习)
读入若干行「名字 分数」,直到输入结束。输出人数、平均分(2 位小数)和最高分学生的名字。
输入 若干行「名字 分数」,读到输入结束。
输出 三行:count=、avg=、top=。
样例输入
Ann 90
Bob 95
Cid 88样例输出
count=3
avg=91.00
top=Bob💡 提示 先读名字、再读分数(scanf("%31s %d", ...) == 2),读到读不动为止。
✅ 查看参考答案与解析
#include <stdio.h>
#include <string.h>
int main(void) {
char name[32];
char best_name[32] = "";
int score = 0;
int best = -1;
int count = 0;
long long sum = 0;
while (scanf("%31s %d", name, &score) == 2) {
++count;
sum += score;
if (score > best) {
best = score;
strcpy(best_name, name);
}
}
if (count == 0) {
return 0;
}
printf("count=%d\n", count);
printf("avg=%.2f\n", (double)sum / count);
printf("top=%s\n", best_name);
return 0;
}解析 这道题把整条 C 线串起来了:字符串数组、循环、函数、strcpy、格式化输出。写完它,C 的基础部分就算过关。