首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言輸入函數實戰演練:從理論到代碼的字符與字符串讀取最佳實踐

     編輯:關於C

實戰場景一:字符級交互與計數器

#include 

int 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*,行緩衝特性
  • 輸入 aEnter 實際產生 2 個字符:'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

最佳實踐總結

  1. 統一用 fgets 讀行,配合 sscanf/strtok/手動解析
  2. 緩衝區大小常數化(#define LINE_MAX 4096),便於調整
  3. 檢查返回值,區分 EOF、錯誤、正常
  4. 移除尾部 \n 作為標準預處理步驟
  5. 動態內存處理超長行,避免截斷數據
  6. 禁用 gets,代碼審查視為嚴重缺陷

輸入處理看似基礎,實則是程序與外界交互的第一道防線。把 fgets 吃透,把緩衝區機制想清,把邊界條件測透,代碼才能在真實世界中穩健運行。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved