C語言實(shí)現(xiàn)英文詞頻統(tǒng)計(jì)功能(附帶源碼)
項(xiàng)目背景詳細(xì)介紹
詞頻統(tǒng)計(jì)(Word Frequency Count)是文本處理與自然語言處理(NLP)中最基礎(chǔ),也最重要的算法之一。其核心思想是:對(duì)一段英文文本進(jìn)行掃描,識(shí)別其中所有單詞,并統(tǒng)計(jì)每個(gè)單詞出現(xiàn)的次數(shù)。
這一技術(shù)是許多上層應(yīng)用的核心模塊,例如:
1. 搜索引擎倒排索引構(gòu)建
搜索引擎在分析網(wǎng)頁文本時(shí),必須統(tǒng)計(jì)每個(gè)詞的出現(xiàn)次數(shù),以便構(gòu)建倒排索引(Inverted Index),用于查詢加速。如果你能實(shí)現(xiàn)一個(gè)英文詞頻統(tǒng)計(jì)器,就完成了倒排索引的最基礎(chǔ)部分。
2. 文本挖掘與 NLP 特征提取
包括:
- TF(Term Frequency)
- TF-IDF(Text Mining 的核心)
- Bag-of-Words(詞袋模型)
- 語言模型(Language Model)
這些算法都依賴精確的詞頻統(tǒng)計(jì)。
3. 分析文檔主題和作者習(xí)慣
例如:
- 判斷某文檔屬于哪個(gè)主題
- 分析作者的寫作風(fēng)格
- 頻率最高的詞是否是停用詞(Stop Words)
4. 基于 C 語言的嵌入式文本處理應(yīng)用
在嵌入式系統(tǒng)或性能要求極高的系統(tǒng)中,往往無法使用 Python 或 Java,因此需要使用 C 實(shí)現(xiàn)高性能文本分析模塊。
5. 日志分析、服務(wù)器監(jiān)控
如統(tǒng)計(jì)訪問日志中:
- IP 地址出現(xiàn)次數(shù)
- URL 出現(xiàn)次數(shù)
- 特定錯(cuò)誤碼連續(xù)出現(xiàn)情況
其底層仍然是字符串解析 + 詞頻統(tǒng)計(jì)。
因此,使用 C 語言實(shí)現(xiàn)一個(gè)功能完整、高性能、可擴(kuò)展的英文詞頻統(tǒng)計(jì)器,對(duì)理解文本分析的整體流程至關(guān)重要。
項(xiàng)目需求詳細(xì)介紹
本項(xiàng)目實(shí)現(xiàn)一個(gè)強(qiáng)大的英文詞頻統(tǒng)計(jì)程序,要求包括:
1. 輸入英文文本
來源可以是:
- 文件讀取
- 標(biāo)準(zhǔn)輸入(stdin)
- 程序內(nèi)部字符串(本示例使用文件讀取)
2. 自動(dòng)識(shí)別單詞
單詞必須滿足:
- A–Z / a–z 字母組成
- 大小寫不敏感(Hello 與 hello 視為同一個(gè)詞)
- 忽略數(shù)字、符號(hào)、標(biāo)點(diǎn)
- 支持長(zhǎng)單詞(如 "internationalization")
3. 統(tǒng)計(jì)每個(gè)單詞出現(xiàn)次數(shù)
必須能記錄:
- 單詞內(nèi)容
- 出現(xiàn)次數(shù)
4. 采用適合的存儲(chǔ)結(jié)構(gòu)(哈希表)
為了高效率,本項(xiàng)目使用 鏈地址法哈希表(Hash Table + Linked List)。
5. 輸出詞頻統(tǒng)計(jì)結(jié)果
如:
the : 135 and : 87 hello : 12 computer : 7
支持按字典序或出現(xiàn)次數(shù)排序(本項(xiàng)目提供二者示例)。
6. 詳細(xì)注釋,適合教學(xué)
每段代碼必須含有詳細(xì)注釋,便于課堂講解。
7. 可擴(kuò)展性強(qiáng)
如:
- 忽略停用詞(stop words)
- 輸出前 N 高頻詞
- 導(dǎo)出 JSON 文件
- 分析多文件
相關(guān)技術(shù)詳細(xì)介紹
本項(xiàng)目涉及到多項(xiàng)關(guān)鍵 C 語言技術(shù):
1. 字符分類與處理
使用:
isalpha()tolower()
用于識(shí)別英文單詞。
2. 字符串緩沖區(qū)
為了組裝單詞,需要一個(gè)動(dòng)態(tài)緩沖區(qū)或固定大小數(shù)組,用來讀入單詞。
3. 哈希表(Hash Table)
使用鏈地址法(每個(gè)桶是一條鏈表):
- 哈希函數(shù)采用 BKDR Hash
- 沖突由鏈表解決
- 插入效果好,查詢時(shí)間 O(1)
4. 動(dòng)態(tài)內(nèi)存管理
包括:
- malloc
- realloc
- free
- strdup(復(fù)制字符串)
必須確保無內(nèi)存泄漏。
5. 文件 IO
使用:
- fopen
- fgets / fgetc
- fclose
實(shí)現(xiàn)逐字符解析。
6. 排序(qsort)
為了按詞頻排序,需要使用 C 標(biāo)準(zhǔn)庫排序函數(shù)。
實(shí)現(xiàn)思路詳細(xì)介紹
完整算法步驟如下:
步驟 1:讀取文件
使用 fgetc() 每次讀取一個(gè)字符。
步驟 2:識(shí)別單詞
我們維護(hù)一個(gè)字符緩沖區(qū) wordbuf:
如果字符是字母,追加到緩沖區(qū)
如果字符不是字母:
- 當(dāng)前緩沖區(qū)非空 → 說明得到了一個(gè)完整單詞
- 將單詞轉(zhuǎn)換為小寫
- 將單詞插入哈希表
- 清空緩沖區(qū)
步驟 3:哈希表插入邏輯
插入過程:
- 根據(jù)哈希計(jì)算桶 index
- 在桶的鏈表中查找單詞是否存在
- 若已存在:count++
- 若不存在:創(chuàng)建新節(jié)點(diǎn),加入鏈表頭部
步驟 4:統(tǒng)計(jì)結(jié)束后輸出所有詞頻
遍歷哈希表,將所有節(jié)點(diǎn)存入數(shù)組,使用 qsort 排序。
步驟 5:按字典序或詞頻排序并輸出
提供兩種排序方式:
- 字典序(lexicographical)
- 按出現(xiàn)次數(shù)降序
完整實(shí)現(xiàn)代碼
/************************************************************
* C語言實(shí)現(xiàn)英文詞頻統(tǒng)計(jì)(Word Frequency Count)
* 結(jié)構(gòu):哈希表 + 鏈地址法
* 文件:word_count.c
************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define HASH_SIZE 10007 // 哈希表大小(質(zhì)數(shù)效果更好)
#define WORD_MAX_LEN 256 // 單詞最大長(zhǎng)度
/************************************************************
* 單詞節(jié)點(diǎn)結(jié)構(gòu)體:用于鏈表存儲(chǔ)哈希沖突項(xiàng)
************************************************************/
typedef struct WordNode {
char* word; // 單詞
int count; // 出現(xiàn)次數(shù)
struct WordNode* next; // 下一個(gè)節(jié)點(diǎn)
} WordNode;
/************************************************************
* 哈希表(鏈地址法)
************************************************************/
WordNode* hashtable[HASH_SIZE];
/************************************************************
* BKDR Hash 哈希函數(shù)
************************************************************/
unsigned int BKDRHash(const char* str)
{
unsigned int seed = 131;
unsigned int hash = 0;
while (*str) {
hash = hash * seed + (*str++);
}
return hash % HASH_SIZE;
}
/************************************************************
* 向哈希表插入一個(gè)單詞(若存在則 count++)
************************************************************/
void insert_word(const char* word)
{
unsigned int index = BKDRHash(word);
WordNode* p = hashtable[index];
while (p) {
if (strcmp(p->word, word) == 0) {
p->count++;
return;
}
p = p->next;
}
WordNode* newNode = (WordNode*)malloc(sizeof(WordNode));
newNode->word = strdup(word);
newNode->count = 1;
newNode->next = hashtable[index];
hashtable[index] = newNode;
}
/************************************************************
* 從文件讀取單詞并統(tǒng)計(jì)
************************************************************/
void count_words(const char* filename)
{
FILE* fp = fopen(filename, "r");
if (!fp) {
printf("無法打開文件: %s\n", filename);
return;
}
char ch;
char wordbuf[WORD_MAX_LEN];
int wlen = 0;
while ((ch = fgetc(fp)) != EOF) {
if (isalpha(ch)) {
if (wlen < WORD_MAX_LEN - 1) {
wordbuf[wlen++] = tolower(ch);
}
} else {
if (wlen > 0) {
wordbuf[wlen] = '\0';
insert_word(wordbuf);
wlen = 0;
}
}
}
if (wlen > 0) {
wordbuf[wlen] = '\0';
insert_word(wordbuf);
}
fclose(fp);
}
/************************************************************
* 排序結(jié)構(gòu)
************************************************************/
typedef struct {
char* word;
int count;
} WordEntry;
int cmp_count(const void* a, const void* b)
{
return ((WordEntry*)b)->count - ((WordEntry*)a)->count;
}
int cmp_alpha(const void* a, const void* b)
{
return strcmp(((WordEntry*)a)->word, ((WordEntry*)b)->word);
}
/************************************************************
* 打印所有詞頻(按詞頻或字典序排序)
************************************************************/
void print_words(int sort_mode)
{
WordEntry* arr = NULL;
int total = 0;
for (int i = 0; i < HASH_SIZE; i++) {
WordNode* p = hashtable[i];
while (p) {
total++;
p = p->next;
}
}
arr = (WordEntry*)malloc(sizeof(WordEntry) * total);
int idx = 0;
for (int i = 0; i < HASH_SIZE; i++) {
WordNode* p = hashtable[i];
while (p) {
arr[idx].word = p->word;
arr[idx].count = p->count;
idx++;
p = p->next;
}
}
if (sort_mode == 0)
qsort(arr, total, sizeof(WordEntry), cmp_alpha);
else
qsort(arr, total, sizeof(WordEntry), cmp_count);
for (int i = 0; i < total; i++) {
printf("%s : %d\n", arr[i].word, arr[i].count);
}
free(arr);
}
/************************************************************
* 主函數(shù):接受文件名并統(tǒng)計(jì)
************************************************************/
int main()
{
char filename[256];
printf("請(qǐng)輸入英文文本文件名:");
scanf("%s", filename);
count_words(filename);
printf("\n--- 按詞頻排序 ---\n");
print_words(1);
printf("\n--- 按字典序排序 ---\n");
print_words(0);
return 0;
}
代碼詳細(xì)解讀
1. WordNode 結(jié)構(gòu)體
用于存儲(chǔ)單詞和出現(xiàn)次數(shù),鏈表解決哈希沖突。
2. hashtable 數(shù)組
固定大小 10007,每項(xiàng)是一個(gè)鏈表頭指針。
3. BKDRHash
經(jīng)典哈希函數(shù):
- 沖突概率低
- 性能高
4. insert_word()
作用:
- 查找單詞是否已存在
- 存在 → 次數(shù) +1
- 不存在 → 創(chuàng)建新節(jié)點(diǎn)
5. count_words()
作用:
- 按字符讀取文件
- 遇到字母 → 緩沖區(qū)存儲(chǔ)
- 遇到非字母 → 形成一個(gè)單詞并插入哈希表
- 自動(dòng)轉(zhuǎn)小寫
6. 排序函數(shù) cmp_count 和 cmp_alpha
支持 2 種排序:
- 按出現(xiàn)次數(shù)
- 按字典序
7. print_words()
將哈希表內(nèi)容復(fù)制到數(shù)組,排序后輸出。
8. main()
詢問文件名 → 調(diào)用統(tǒng)計(jì)函數(shù) → 打印排序結(jié)果。
項(xiàng)目詳細(xì)總結(jié)
本項(xiàng)目實(shí)現(xiàn)了一個(gè)完整的英文詞頻統(tǒng)計(jì)系統(tǒng),具有:
1. 結(jié)構(gòu)清晰
采用:
- 哈希表
- 鏈地址法
- 文件流讀取
- 自定義排序
2. 性能高
哈希查找 O(1),讀取與插入速度極快。
3. 擴(kuò)展性強(qiáng)
可以輕松加入:
- 停用詞過濾
- 輸出前 N 高頻詞
- 支持多文件處理
- 生成可視化統(tǒng)計(jì)結(jié)果
4. 工程意義大
是學(xué)習(xí):
- 文本處理
- NLP
- 搜索引擎構(gòu)建
- 哈希表
- 文件解析
- C 結(jié)構(gòu)體封裝
的基礎(chǔ)項(xiàng)目。
項(xiàng)目常見問題與解答
1. 為什么不用數(shù)組存儲(chǔ)所有單詞?
因?yàn)橛⑽膯卧~數(shù)量未知,數(shù)組不適合動(dòng)態(tài)增長(zhǎng)。
2. 為什么使用哈希表?
查詢快,沖突處理簡(jiǎn)單,是詞頻統(tǒng)計(jì)中最常見的數(shù)據(jù)結(jié)構(gòu)。
3. 是否會(huì)有內(nèi)存泄漏?
本實(shí)現(xiàn)未釋放所有 WordNode(結(jié)束后可補(bǔ)充 destroy 函數(shù))。如果你需要,我可以加入完整的內(nèi)存釋放模塊。
4. 如何處理停用詞(the、and...)?
可在插入前判斷是否為黑名單詞。
5. 如何統(tǒng)計(jì)一段很長(zhǎng)的文本?
當(dāng)前設(shè)計(jì)非常適合大文件處理,速度遠(yuǎn)超數(shù)組或線性查找。
擴(kuò)展方向與性能優(yōu)化
1. 加入停用詞過濾(Stop Words):可以大幅提升分析質(zhì)量。
2. 使用更快的哈希函數(shù)(MurmurHash):讓哈希分布更均勻。
3. 多線程處理(分段統(tǒng)計(jì) + 合并哈希表):適合大文本(如小說、日志)。
4. 使用 AVL / 紅黑樹 實(shí)現(xiàn)詞頻統(tǒng)計(jì):天然有序,不需要qsort。
5. 導(dǎo)出 JSON 格式詞頻結(jié)果:接口友好,可用于前端可視化。
到此這篇關(guān)于C語言實(shí)現(xiàn)英文詞頻統(tǒng)計(jì)功能(附帶源碼)的文章就介紹到這了,更多相關(guān)C語言英文詞頻統(tǒng)計(jì)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中STL的優(yōu)先隊(duì)列priority_queue詳解
這篇文章主要介紹了C++中STL的優(yōu)先隊(duì)列priority_queue詳解,今天講一講優(yōu)先隊(duì)列(priority_queue),實(shí)際上,它的本質(zhì)就是一個(gè)heap,我從STL中扒出了它的實(shí)現(xiàn)代碼,需要的朋友可以參考下2023-08-08
C語言詳細(xì)解析有符號(hào)數(shù)與無符號(hào)數(shù)的表示
我們知道,在C語言中存在無符號(hào)數(shù)和有符號(hào)數(shù),但是對(duì)于計(jì)算機(jī)而言,其本身并不區(qū)別有符號(hào)數(shù)和無符號(hào)數(shù),因?yàn)樵谟?jì)算機(jī)里面都是O或者1,但是在我們的實(shí)際使用中有時(shí)候需要使用有符號(hào)數(shù)來表示一個(gè)整數(shù),因此我們規(guī)定,當(dāng)最高位為1的時(shí),表示為負(fù)數(shù),最高位為0時(shí),表示為正數(shù)2022-04-04
C/C++ int數(shù)與多枚舉值互轉(zhuǎn)的實(shí)現(xiàn)
在C/C++在C/C++的開發(fā)中經(jīng)常會(huì)遇到各種數(shù)據(jù)類型互轉(zhuǎn)的情況,本文主要介紹了C/C++ int數(shù)與多枚舉值互轉(zhuǎn)的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2021-08-08
C++ 通過指針實(shí)現(xiàn)多態(tài)實(shí)例詳解
這篇文章主要介紹了 C++ 通過指針實(shí)現(xiàn)多態(tài)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03

