字符級與格式化 I/O 的等價轉換與選型指南
核心等價關系
| 功能 | 字符函數 | 格式化函數等價形式 | 關鍵差異 |
| 讀單字符 | c = getchar() | scanf("%c", &c) | scanf 跳過空白需 "%c" 前加空格 " %c" |
| 寫單字符 | putchar(c) | printf("%c", c) | printf 返回輸出字符數,putchar 返回字符值 |
| 讀字符串 | fgets(buf, n, stdin) | scanf("%99s", buf) / scanf("%99[^\n]", buf) | scanf 停於空白,fgets 讀整行含換行 |
| 寫字符串 | puts(str) | printf("%s\n", str) | puts 自動換行,printf 需顯式 \n |
經典循環模式對比
// 模式 1:getchar 讀取直到換行
int c;
while ((c = getchar()) != '\n' && c != EOF) {
putchar(c); // 處理字符
}
// 模式 2:scanf 等價(注意空格跳過空白字符)
int c;
while (scanf(" %c", &c) == 1) { // 格式串前的空格跳過 \n 等空白
if (c == '\n') break;
printf("%c", c);
}
// 模式 3:fgets 讀整行(推薦,安全可控)
char line[1024];
while (fgets(line, sizeof(line), stdin)) {
// 移除末尾換行
line[strcspn(line, "\n")] = '\0';
printf("讀到一行: %s\n", line);
}
關鍵差異深度解析
1. 空白字符處理
// getchar: 讀取所有字符,包括空格、制表符、換行符
// scanf("%c"): 默認也讀取空白字符
// scanf(" %c"): 格式串開頭的空格 = 跳過任意數量空白字符(\n \t 等)
// 實測差異:
// 輸入: "a b\n"
// getchar() 循環: 讀到 'a', ' ', 'b', '\n'
// scanf("%c") 循環: 同 getchar
// scanf(" %c") 循環: 只讀到 'a', 'b' (跳過空格和換行)
2. 返回值語義
| 函數 | 成功返回 | 失敗/EOF 返回 | 典型判斷 |
| getchar() | 字符值 (0-255) | EOF (-1) | c != EOF |
| scanf("%c", &c) | 匹配項數 (1) | EOF (-1) | == 1 |
| fgets() | 緩衝區指針 | NULL | != NULL |
3. 緩衝區與換行殘留問題
// 經典 Bug:scanf 讀數後殘留 \n,後續 getchar/fgets 直接讀到空行
int n;
scanf("%d", &n); // 輸入 "42\n",緩衝區剩 "\n"
char c = getchar(); // 直接讀到 '\n',非預期!
// 解決方案:
// 1. 額外讀取一個字符
scanf("%d", &n);
getchar(); // 吞掉 \n
// 2. 格式串加空格
scanf("%d ", &n); // %d 後的空格跳過後續空白
// 3. 統一用 fgets + sscanf(推薦)
char buf[100];
fgets(buf, sizeof(buf), stdin);
sscanf(buf, "%d", &n);
工程選型建議
| 場景 | 推薦方案 | 理由 |
| 高性能字符流處理 | getchar/putchar + 大緩衝區 | 無格式解析開銷,適合編譯器/解析器 |
| 交互式菜單/單字符命令 | getchar() 無緩衝模式 (termios/raw mode) | 即時響應,無需回車確認 |
| 格式化數據讀寫 | scanf/printf 族 | 類型安全、格式靈活、標準化 |
| 整行文本讀取 | fgets + sscanf/strtok/手工解析 | 安全可控、緩衝區保護、保留換行信息 |
| 二進制數據 | fread/fwrite | 無格式轉換、高性能、跨平台需注意端序 |
安全替代方案總結
// 替代 gets(已移除 C11):
// fgets(buf, sizeof(buf), stdin);
// buf[strcspn(buf, "\n")] = '\0';
// 替代 scanf 讀字符串(防溢出):
// scanf("%99s", buf); // 限制寬度
// 或 fgets + sscanf
// 替代 scanf 讀單字符(避免空白困擾):
// " %c" 格式串前加空格
// 或 getchar() 直接控制
- 上一篇文章: C語言浮點數四捨五入輸出:printf 格式控制與數值處理
- 下一篇文章: C語言基礎進階學習路線:從語法到工程化的必讀書單與實戰建議
Copyright ©
程式碼之家 All Rights Reserved