| 難點 | 解決方案 |
| 文本輸入方式 | 採用「先寫入臨時文件,再多次讀取」模式,避免標準輸入無法重讀問題 |
| 單詞分割規則 | isspace() + ispunct() 作為分隔符,連續非分隔符視為一個單詞 |
| 大小寫敏感 | 題目未指定,採用**大小寫敏感** (區分 'A'/'a'),易於修改為 tolower() 統一 |
| 緩衝區管理 | 文件操作用 fread 逐字符讀取,避免 scanf 格式化陷阱 |
完整標準化代碼 (C99 風格、模塊化、錯誤處理)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#define MAX_WORD_LEN 100
#define TEMP_FILE "temp_text.dat"
void count_ab(void);
void count_total_words(void);
void count_target_word(void);
void write_text_to_file(void);
int main(void) {
write_text_to_file(); // 統一入口:先獲取文本存文件
int choice;
while (true) {
printf("\n=== 字符串處理菜單 ===\n");
printf("1. 統計 'a' 和 'b' 出現次數\n");
printf("2. 統計單詞總數\n");
printf("3. 統計指定單詞出現次數\n");
printf("4. 退出\n");
printf("選擇 (1-4): ");
if (scanf("%d", &choice) != 1) { // 輸入非數字
scanf("%*[^\n]"); getchar(); // 清理緩衝區
puts("輸入無效,請輸入數字 1-4");
continue;
}
switch (choice) {
case 1: count_ab(); break;
case 2: count_total_words(); break;
case 3: count_target_word(); break;
case 4: puts("再見!"); return 0;
default: puts("選項範圍 1-4");
}
}
}
// 1. 統計 a/b
void count_ab(void) {
FILE *fp = fopen(TEMP_FILE, "r");
if (!fp) { perror("打開文件失敗"); return; }
int cnt_a = 0, cnt_b = 0;
int ch;
while ((ch = fgetc(fp)) != EOF) {
if (ch == 'a') cnt_a++;
else if (ch == 'b') cnt_b++;
}
fclose(fp);
printf("'a' 出現 %d 次\n'b' 出現 %d 次\n", cnt_a, cnt_b);
}
// 2. 統計單詞總數 (狀態機法)
void count_total_words(void) {
FILE *fp = fopen(TEMP_FILE, "r");
if (!fp) { perror("打開文件失敗"); return; }
int count = 0;
bool in_word = false;
int ch;
while ((ch = fgetc(fp)) != EOF) {
if (isspace(ch) || ispunct(ch)) {
in_word = false;
} else if (!in_word) {
in_word = true;
count++;
}
}
fclose(fp);
printf("單詞總數: %d\n", count);
}
// 3. 統計目標單詞
void count_target_word(void) {
char target[MAX_WORD_LEN];
printf("輸入目標單詞: ");
if (scanf("%99s", target) != 1) return; // 讀取單詞 (自動跳過空白)
FILE *fp = fopen(TEMP_FILE, "r");
if (!fp) { perror("打開文件失敗"); return; }
int count = 0;
char word[MAX_WORD_LEN];
int idx = 0;
int ch;
while ((ch = fgetc(fp)) != EOF) {
if (isspace(ch) || ispunct(ch)) {
if (idx > 0) {
word[idx] = '\0';
if (strcmp(word, target) == 0) count++;
idx = 0;
}
} else {
if (idx < MAX_WORD_LEN - 1) word[idx++] = ch;
}
}
// 處理文件末尾單詞
if (idx > 0) {
word[idx] = '\0';
if (strcmp(word, target) == 0) count++;
}
fclose(fp);
printf("'%s' 出現 %d 次\n", target, count);
}
// 文本輸入 → 文件
void write_text_to_file(void) {
FILE *fp = fopen(TEMP_FILE, "w");
if (!fp) { perror("創建文件失敗"); exit(EXIT_FAILURE); }
puts("請輸入文本 (Ctrl+D / Ctrl+Z 結束):");
int ch;
while ((ch = getchar()) != EOF) {
fputc(ch, fp);
}
fclose(fp);
puts("文本已保存。");
}
關鍵算法解析
單詞計數狀態機
in_word = false
for each char c:
if c is separator:
in_word = false
else if not in_word:
in_word = true
count++
優勢:O(n) 時間、O(1) 空間、正確處理多重分隔符、行首行尾、標點符號。
目標單詞匹配
逐字符構建當前單詞,遇到分隔符或 EOF 時與目標 strcmp 比較。避免了 strstr 可能產生的子串誤匹配 (如 "the" 匹配 "other")。
測試用例與驗收標準
| 輸入文本 | 功能 1 (a/b) | 功能 2 (總詞數) | 功能 3 (目標 "the") |
a b c\napple banana | a:2, b:2 | 4 (a, b, c, apple, banana → 5? 實際分割: "a" "b" "c" "apple" "banana" = 5) | 0 |
The quick brown fox jumps over the lazy dog. | a:1, b:1 | 9 | 2 (The, the — 大小寫敏感則 1) |
hello,world! hello...world | a:0, b:0 | 4 (hello, world, hello, world) | 0 |
| 空文件 | 0, 0 | 0 | 0 |
實訓報告撰寫提綱 (按評分項)
- 流程圖與算法描述 (25分):
- 主流程圖:輸入文本→寫文件→菜單循環→分支功能→輸出結果
- 三大核心函數流程圖:字符計數、狀態機單詞計數、單詞構建匹配
- 算法文字描述:時間/空間複雜度、邊界處理、狀態轉移條件
- 知識點描述 (15分):文件操作 (fopen/fclose/fgetc/fputc)、字符分類 (ctype.h)、字符串比較 (strcmp)、狀態機模式、緩衝區處理、EOF 判斷、錯誤碼返回
- 難點與解決 (10分):標準輸入不可重讀 → 文件中轉;單詞定義模糊 → 明確分隔符集;輸入緩衝區殘留 →
scanf("%*[^\n]") 清理
- 學習總結 (10分):模塊化設計思想、防御性編程習慣、測試驅動思維、C 標準庫熟悉度提升
進階優化方向
- 內存映射文件 (
mmap) 替換逐字符讀取,大文件性能提升
- Trie 樹 / Aho-Corasick 算法支持多模式同時匹配
- Unicode/UTF-8 支持:
wchar_t/char16_t + ICU 庫
- 並行化:MapReduce 風格分塊統計再歸併
總結
本方案提供了**標準化、模塊化、可測試、可擴展**的完整實現。核心亮點:
- 文件中轉解決標準輸入不可重讀
- 狀態機單詞計數 O(1) 空間
- 逐字符構建單詞精確匹配避免子串干擾
- 完整錯誤處理與輸入驗證
- 清晰分離 I/O、業務邏輯、用戶交互
代碼可直接作為實訓報告附件,配合上述報告提綱,滿足所有評分要求。
- 上一篇文章: C語言結構體定義兩種形式差異詳解:typedef 匿名結構體 vs 標籤結構體
- 下一篇文章: C語言緊急作業求助:正確提問姿勢與常見問題快速排查清單
Copyright ©
程式碼之家 All Rights Reserved