#includeint main() { int ch; // 必須用 int 接收 EOF int count = 0; puts("請輸入字符 (Ctrl+D/Ctrl+Z 結束):"); while (EOF != (ch = fgetc(stdin))) { printf("第 %d 個字符: '%c' (ASCII: %d)\n", ++count, ch, ch); if (count >= 10) { // 防止無限演示 puts("演示結束,已讀取 10 個字符"); break; } } printf("總計讀取: %d 個字符\n", count); return 0; }
關鍵點:
int ch 而非 char,正確判斷 EOF(通常為 -1)stdin 同樣是 FILE*,行緩衝特性'a' 和 '\n'#include#include #define BUFSIZ 1024 void safe_echo(const char *prompt) { char buf[BUFSIZ]; printf("%s", prompt); if (fgets(buf, sizeof(buf), stdin)) { // 移除尾部換行符 size_t len = strlen(buf); if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0'; printf(">>> 您輸入: [%s]\n", buf); } else { puts("讀取失敗或 EOF"); } } int main() { safe_echo("請輸入一行文本: "); safe_echo("再輸入一行: "); return 0; }
對比 gets 危險性演示(僅教學環境嘗試):
// 危險代碼 - 僅為理解漏洞
char tiny[8];
puts("請輸入超過 7 字符的長文本 (演示緩衝區溢出):");
gets(tiny); // 輸入 "123456789" 將覆蓋棧內存
printf("tiny = %s\n", tiny);
現代編譯器會拒絕編譯或強烈警告,運行時觸發棧保護機制崩潰。
#include#include #include int main(int argc, char *argv[]) { FILE *fp = stdin; // 默認標準輸入 if (argc > 1) { fp = fopen(argv[1], "r"); if (!fp) { perror("打開文件失敗"); return 1; } } char line[4096]; int lineno = 0; while (fgets(line, sizeof(line), fp)) { lineno++; // 去除換行符 char *nl = strchr(line, '\n'); if (nl) *nl = '\0'; // 處理邏輯:這裡只演示帶行號輸出 printf("%4d: %s\n", lineno, line); } if (fp != stdin) fclose(fp); return 0; }
編譯後可作為管道過濾器:
$ gcc line_processor.c -o lp $ echo -e "第一行\n第二行\n第三行" | ./lp 1: 第一行 2: 第二行 3: 第三行 $ ./lp /etc/passwd | head -5 1: root:x:0:0:root:/root:/bin/bash 2: daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin ...
fgets 讀取超長行的分段處理當行長度超過緩衝區時,fgets 會分段讀取(不含 \n 的段)。檢測邏輯:
char buf[256];
char *full_line = NULL;
size_t full_len = 0, full_cap = 0;
while (fgets(buf, sizeof(buf), stdin)) {
size_t len = strlen(buf);
bool line_end = (len > 0 && buf[len-1] == '\n');
if (line_end) buf[--len] = '\0'; // 移除 \n
// 動態擴容拼接
if (full_len + len + 1 > full_cap) {
full_cap = (full_cap == 0) ? 512 : full_cap * 2;
full_line = realloc(full_line, full_cap);
}
memcpy(full_line + full_len, buf, len + 1); // 包含 \0
full_len += len;
if (line_end) {
printf("完整行 (%zu 字節): %s\n", full_len, full_line);
full_len = 0; // 重置處理下一行
}
}
free(full_line);
| 現象 | 原因 | 修正 |
|---|---|---|
| 程序跳過輸入 | 前序 scanf 留下 \n 在緩衝區 | scanf 後調用 getchar() 吸收,或統一用 fgets+sscanf |
| 輸出多空行 | fgets 保留 \n,配合 puts/printf("\n") | 移除 \n 或用 fputs |
| 無法檢測 EOF | 用 char 接收 getchar() | 用 int 接收 |
| 緩衝區溢出崩潰 | 使用 gets 或 scanf("%s") 無長度限制 | fgets / scanf("%99s") |
| 最後一行遺漏 | 文件無尾隨 \n,fgets 仍返回該行 | 正常處理,只需檢查返回值非 NULL |
fgets 讀行,配合 sscanf/strtok/手動解析#define LINE_MAX 4096),便於調整\n 作為標準預處理步驟gets,代碼審查視為嚴重缺陷輸入處理看似基礎,實則是程序與外界交互的第一道防線。把 fgets 吃透,把緩衝區機制想清,把邊界條件測透,代碼才能在真實世界中穩健運行。