統計一行文本中單詞個數(單詞定義為由空格分隔的字符序列)是 C 語言處理字符流、狀態機思想的入門經典。本文提供流式字符處理法與緩存數組處理法兩種完整實現,並分析邊界條件、擴展性與工程應用場景。
掃描字符流,維護一個狀態變量 in_word(0=在空格/分隔符,1=在單詞內):
in_word==0 → 進入單詞,count++,in_word=1。in_word=0。#includeint main() { int ch; // 必須用 int 接收 getchar(),以容納 EOF int count = 0; int in_word = 0; // 0: 當前在分隔符區,1: 當前在單詞內 while ((ch = getchar()) != '\n' && ch != EOF) { // 讀到換行或文件結束停止 if (ch == ' ') { in_word = 0; // 遇到空格,標記為分隔符狀態 } else if (!in_word) { // 非空格且之前不在單詞中 in_word = 1; // 進入單詞狀態 count++; // 單詞計數 +1 } // else: 非空格且 in_word==1,單詞內部,不計數 } printf("單詞數: %d\n", count); return 0; }
優點:O(1) 空間,可處理任意長輸入行(受限於輸入緩衝區),適合管道/重定向輸入。
適用場景:流式日誌分析、嵌入式系統內存受限環境。
#include#include int count_words(const char *text, int len) { int count = 0; int in_word = 0; for (int i = 0; i < len; i++) { if (text[i] == ' ') { in_word = 0; } else if (!in_word) { in_word = 1; count++; } } return count; } int main() { char line[1024]; // 固定緩衝區,足夠大多數場景 int idx = 0; int ch; // 讀取一行(含空格),排除換行符 while ((ch = getchar()) != '\n' && ch != EOF && idx < 1023) { line[idx++] = ch; } line[idx] = '\0'; // 手動添加結束符 printf("單詞數: %d\n", count_words(line, strlen(line))); return 0; }
優點:代碼結構清晰,分離「輸入」與「處理」,便於單元測試 count_words 函數,支持對同一行多次分析。
缺點:固定緩衝區可能截斷超長行(可用動態擴展 realloc 解決)。
| 輸入 | 預期輸出 | 說明 |
|---|---|---|
"" (空行) | 0 | 無字符 |
" " (多空格) | 0 | 全分隔符 |
"hello" | 1 | 單詞無尾隨空格 |
"hello world" | 2 | 單空格分隔 |
" hello world " | 2 | 首尾及多重空格 |
"a b c" | 3 | 單字符單詞 |
"it's a test" | 3 | 撇號屬於單詞內部(非空格) |
若單詞分隔符不僅是空格,還包括 \t, \n, ,, ., ; 等:
int is_delim(char c) {
return c == ' ' || c == '\t' || c == '\n' || c == ',' || c == '.';
}
// 在 FSM 中替換判斷條件
if (is_delim(ch)) in_word = 0;
else if (!in_word) { in_word = 1; count++; }
| 維度 | 流式字符法 | 緩存數組法 |
|---|---|---|
| 空間複雜度 | O(1) | O(N) (行長度) |
| 最大行長限制 | 無(受限於系統輸入緩衝) | 有(緩衝區大小) |
| 代碼模塊化 | 較低(IO與邏輯耦合) | 高(可提取純函數) |
| 適用場景 | 流處理、內存受限、管道輸入 | 交互式程序、需多次掃描、教學演示 |
| 擴展性 | 需修改主循環 | 只需修改 count_words |
核心收穫:單詞計數本質是字符流上的二狀態自動機。掌握狀態變量設計、邊界觸發計數(進入單詞瞬間而非離開)、輸入讀取邊界(
EOF/\n/緩衝區滿),即可舉一反三解決「統計行數」「提取第 k 個單詞」「分割字符串」等同類問題。工程中推薦緩存數組法 + 純函數,利於測試與維護;系統級/流式場景用流式字符法。