這是一個經典的C語言文件處理練習:同時打開兩個文本文件,交替輸出它們的第1行、第2行...直到較長文件讀完。這不僅考驗文件操作基礎,更涉及EOF處理、循環控制、邊界條件等核心技能。
/* 原版核心邏輯 */
while(1) {
/* 讀文件1的一行 */
while((ch1=getc(wj1)) != '\n') {
if (ch1 == EOF) break;
putc(ch1, stdout);
}
if (ch1 != EOF) putchar('\n');
/* 讀文件2的一行 */
while((ch2=getc(wj2)) != '\n') {
if (ch2 == EOF) break;
putc(ch2, stdout);
}
if (ch2 != EOF) putchar('\n');
if (ch1 == EOF && ch2 == EOF) return 0;
}
while(1)配合內部break不夠優雅#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_PATH 4096
/* 安全讀取一行並輸出,返回值:1=正常行 0=EOF -1=錯誤 */
int read_and_print_line(FILE *fp) {
int ch;
int has_content = 0;
while ((ch = getc(fp)) != EOF) {
has_content = 1;
if (ch == '\n') {
putchar('\n');
return 1; // 完整行結束
}
putchar(ch);
}
/* 處理文件結束情況 */
if (has_content) {
/* 文件末尾無換行符的最後一行 */
putchar('\n');
return 1;
}
return 0; // 純EOF,無內容
}
int main(void) {
FILE *fp1 = NULL, *fp2 = NULL;
char name1[MAX_PATH], name2[MAX_PATH];
/* 安全獲取文件名 */
printf("請輸入第一個文件名: ");
if (!fgets(name1, sizeof(name1), stdin)) return EXIT_FAILURE;
name1[strcspn(name1, "\n")] = '\0';
printf("請輸入第二個文件名: ");
if (!fgets(name2, sizeof(name2), stdin)) return EXIT_FAILURE;
name2[strcspn(name2, "\n")] = '\0';
/* 打開文件 */
fp1 = fopen(name1, "r");
if (!fp1) { perror(name1); return EXIT_FAILURE; }
fp2 = fopen(name2, "r");
if (!fp2) { perror(name2); fclose(fp1); return EXIT_FAILURE; }
/* 交替讀取主循環 */
int ret1 = 1, ret2 = 1;
while (ret1 || ret2) {
ret1 = read_and_print_line(fp1);
ret2 = read_and_print_line(fp2);
/* 任一文件出錯提前退出 */
if (ret1 == -1 || ret2 == -1) break;
}
fclose(fp1);
fclose(fp2);
return 0;
}
將「讀一行並輸出」封裝為獨立函數read_and_print_line,返回狀態碼清晰表達三種結果:正常行、末尾行、純EOF。主循環邏輯瞬間清晰。
POSIX標準要求文本文件以換行符結尾,但現實中大量文件不符合。優化版通過has_content標記,確保最後一行內容即使無\n也能正確輸出並補上換行。
fgets替代gets,指定緩衝區上限strcspn高效移除換行符,比手寫循環更標準4096適應現代文件系統錯誤路徑上正確關閉已打開的文件指針,避免洩漏。
/* 可變參數版本:支持任意數量文件交替輸出 */
#include <stdarg.h>
void alternate_print_files(int count, ...) {
va_list ap;
FILE **files = malloc(count * sizeof(FILE*));
va_start(ap, count);
for (int i = 0; i < count; i++) {
const char *name = va_arg(ap, const char*);
files[i] = fopen(name, "r");
if (!files[i]) { perror(name); /* 清理已打開文件 */ }
}
va_end(ap);
int active = count;
while (active > 0) {
active = 0;
for (int i = 0; i < count; i++) {
if (files[i] && read_and_print_line(files[i])) active++;
}
}
/* 關閉所有文件並釋放內存 */
}
這個練習看似簡單,實則蘊含「邊界條件處理」、「資源安全管理」、「模塊化設計」等工程核心素養。從「能跑通」到「寫得穩、改得動、用得放心」,正是C程序員進階的必經之路。