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

C 語言統計單詞長度與字元頻率:直方圖實現完解析(K&R 習題精講)

     編輯:關於C語言

緣起:K&R 經典習題

《C 程序設計語言》(K&R)第 1 章習題要求:

  1. 編寫程序,打印輸入中**單詞長度**的直方圖。
  2. 編寫程序,打印輸入中**各字元出現頻率**的直方圖。

本文以題目 1 為主線,詳細拆解「狀態機 + 計數陣列」的核心思路,並給出完整、可運行、符合現代規範的代碼。

核心思路:有限狀態自動機(FSA)

輸入流可視為字元序列。單詞邊界由**空白字元**(空格、換行、製表符)分隔。掃描時只需兩個狀態:

  • IN:正在讀取單詞內部(未遇到空白)
  • OUT:處於單詞之間(剛遇到空白)

狀態轉移:

    遇到非空白且狀態為 OUT → 進入 IN,單詞長度計數器 nc = 1 遇到非空白且狀態為 IN → nc++ 遇到空白且狀態為 IN → 歸檔長度 nc,狀態轉 OUT,nc = 0 遇到空白且狀態為 OUT → 保持 OUT

資料結構設計

  • MAXWORD:假設單詞最大長度(如 30)。超長單詞歸入 overflow 計數。
  • wl[MAXWORD + 1]:wl[i] 存儲長度為 i 的單詞個數。索引 0 不用,對齊語義。
  • overflow:長度超過 MAXWORD 的單詞計數。

完整代碼(C99 標準,含註釋)

#include <stdio.h>

#define MAXWORD 30   // 單詞最大長度,可調大
#define IN      1    // 在單詞內
#define OUT     0    // 在單詞外

int main(void) {
    int c;               // 當前字元(用 int 接受 EOF)
    int nc = 0;          // 當前單詞長度
    int state = OUT;     // 狀態
    int overflow = 0;    // 超長單詞計數
    int wl[MAXWORD + 1]; // 長度統計陣列,wl[0] 不用

    // 初始化
    for (int i = 1; i <= MAXWORD; ++i) wl[i] = 0;

    // 掃描輸入流
    while ((c = getchar()) != EOF) {
        if (c == ' ' || c == '\n' || c == '\t') { // 遇到分隔符
            if (state == IN) {                    // 剛結束一個單詞
                if (nc <= MAXWORD) ++wl[nc];
                else ++overflow;
                nc = 0;
            }
            state = OUT;
        } else {                                   // 非分隔符
            if (state == OUT) {                    // 進入新單詞
                state = IN;
                nc = 1;
            } else {                               // 繼續當前單詞
                ++nc;
            }
        }
    }

    // 輸出直方圖(橫向數字版,易於驗證)
    printf("單詞長度分佈直方圖:\n");
    for (int i = 1; i <= MAXWORD; ++i) {
        if (wl[i] > 0) {
            printf("%2d: %3d ", i, wl[i]);
            // 可選:打印星號條
            // for (int k = 0; k < wl[i]; ++k) putchar('*');
            putchar('\n');
        }
    }
    if (overflow) printf("超過 %d 字元的單詞:%d 個\n", MAXWORD, overflow);

    return 0;
}

關鍵細節解析

  1. getchar() 返回 int:必須用 int c 接收,以區分 EOF(通常為 -1)與合法字元(0~255)。
  2. 陣列索引從 1 開始:wl[1] 對應長度 1,語義直觀,wl[0] 棄用。
  3. 邊界處理:輸入結束時若處於 IN 狀態,最後一個單詞不會被統計。修正:循環結束後補一段:
    if (state == IN) { // 處理文件末尾無分隔符的情況
        if (nc <= MAXWORD) ++wl[nc];
        else ++overflow;
    }
  4. 直方圖視覺化:可將數字改為星號條,或縱向打印(需先求最大值再分行)。

題目 2:字元頻率直方圖(思路同構)

只需將「單詞長度」換為「字元 ASCII 碼」,狀態機簡化為無狀態計數:

int freq[256] = {0}; // 覆蓋擴展 ASCII
while ((c = getchar()) != EOF) ++freq[(unsigned char)c];
// 輸出可見字元頻率
for (int i = 32; i < 127; ++i)
    if (freq[i]) printf("'%c' (%3d): %d\n", i, i, freq[i]);

現代化擴展建議

  • 動態陣列:用 realloc 替代固定 MAXWORD,支援任意長度單詞。
  • Unicode/UTF-8:將 char 升級為 uint32_t(Code Point),使用 icu 或手寫 UTF-8 解碼器統計字素簇長度。
  • 多執行緒:大文件可分塊並行統計,最後合併陣列(Map-Reduce 思想)。
  • 單元測試:構造已知輸入(含邊界:空文件、無分隔符、超長單詞),驗證輸出。

總結

K&R 習題看似簡單,實則蘊含流式處理、有限狀態機、計數統計、邊界條件等核心程式設計思想。掌握「狀態 + 計數器」模式,不僅能解決直方圖問題,更是詞法分析、協議解析、日誌統計等眾多場景的基石。

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