近期研讀《C 語言程式設計 第二版》時,嘗試完成書中習題,其中兩題特別具有代表性:
本文詳細記錄第一題的解題思路與程式碼實作,第二題邏輯類似,讀者可自行延伸。
程式需讀取標準輸入,以空白字元(空格、換行、製表符)作為單詞分隔符。每當讀完一個單詞,即統計其長度;輸入結束(EOF)時,輸出各長度單詞的出現次數。
例如輸入:
i am a student
輸出應顯示:長度 1 的單詞 2 個(i, a),長度 2 的單詞 1 個(am),長度 7 的單詞 1 個(student)。
單詞長度有上限。根據維基百科,英語最長單詞約 30 字母,故定義 MAXWORD = 30。使用陣列 wl[MAXWORD + 1] 記錄長度為 i 的單詞計數(索引 0 不用)。初始化時將所有元素歸零。
採用「狀態機」處理輸入流:
IN:正在讀取單詞內部。OUT:位於單詞之外(遇到分隔符)。變數 nc 累計目前單詞長度。讀到分隔符且 nc > 0 時,表示一個單詞結束,將 wl[nc]++(超過 MAXWORD 另計 overflow),並重置 nc = 0、狀態轉為 OUT。讀到非分隔符且狀態為 OUT 時,進入 IN,nc = 1;若已在 IN,則 nc++。
#include <stdio.h>
#define MAXWORD 30 /* 單詞最大長度 */
#define IN 1 /* 在單詞內 */
#define OUT 0 /* 在單詞外 */
int word_length_histogram(void)
{
int c, i, nc, state, overflow;
int wl[MAXWORD + 1]; /* 索引 1..MAXWORD */
state = OUT;
nc = 0;
overflow = 0;
for (i = 1; i <= MAXWORD; ++i)
wl[i] = 0;
while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\n' || c == '\t') {
state = OUT;
if (nc > 0) {
if (nc <= MAXWORD)
++wl[nc];
else
++overflow;
nc = 0;
}
} else if (state == OUT) {
state = IN;
nc = 1;
} else {
++nc;
}
}
for (i = 1; i <= MAXWORD; ++i)
printf("長度為 %2d 的單詞個數:%3d\n", i, wl[i]);
if (overflow)
printf("長度超過 %d 的單詞:%d\n", MAXWORD, overflow);
return 0;
}
int main(void)
{
return word_length_histogram();
}
編譯執行後,輸入文字並以 Ctrl+D (Linux/macOS) 或 Ctrl+Z (Windows) 送出 EOF:
$ ./a.out
The quick brown fox jumps over the lazy dog
^D
長度為 1 的單詞個數: 0
長度為 2 的單詞個數: 0
長度為 3 的單詞個數: 4
長度為 4 的單詞個數: 1
長度為 5 的單詞個數: 3
長度為 6 的單詞個數: 1
長度超過 30 的單詞:0
第二題「字元頻率直方圖」只需將陣列大小擴充至 256(涵蓋所有 ASCII 碼),並在讀取每個字元時直接 ++freq[c],邏輯更簡單。讀者可嘗試自行實作。