C语言实现英文词频统计功能(附带源码)
项目背景详细介绍
词频统计(Word Frequency Count)是文本处理与自然语言处理(NLP)中最基础,也最重要的算法之一。其核心思想是:对一段英文文本进行扫描,识别其中所有单词,并统计每个单词出现的次数。
这一技术是许多上层应用的核心模块,例如:
1. 搜索引擎倒排索引构建
搜索引擎在分析网页文本时,必须统计每个词的出现次数,以便构建倒排索引(Inverted Index),用于查询加速。如果你能实现一个英文词频统计器,就完成了倒排索引的最基础部分。
2. 文本挖掘与 NLP 特征提取
包括:
- TF(Term Frequency)
- TF-IDF(Text Mining 的核心)
- Bag-of-Words(词袋模型)
- 语言模型(Language Model)
这些算法都依赖精确的词频统计。
3. 分析文档主题和作者习惯
例如:
- 判断某文档属于哪个主题
- 分析作者的写作风格
- 频率最高的词是否是停用词(Stop Words)
4. 基于 C 语言的嵌入式文本处理应用
在嵌入式系统或性能要求极高的系统中,往往无法使用 Python 或 Java,因此需要使用 C 实现高性能文本分析模块。
5. 日志分析、服务器监控
如统计访问日志中:
- IP 地址出现次数
- URL 出现次数
- 特定错误码连续出现情况
其底层仍然是字符串解析 + 词频统计。
因此,使用 C 语言实现一个功能完整、高性能、可扩展的英文词频统计器,对理解文本分析的整体流程至关重要。
项目需求详细介绍
本项目实现一个强大的英文词频统计程序,要求包括:
1. 输入英文文本
来源可以是:
- 文件读取
- 标准输入(stdin)
- 程序内部字符串(本示例使用文件读取)
2. 自动识别单词
单词必须满足:
- A–Z / a–z 字母组成
- 大小写不敏感(Hello 与 hello 视为同一个词)
- 忽略数字、符号、标点
- 支持长单词(如 "internationalization")
3. 统计每个单词出现次数
必须能记录:
- 单词内容
- 出现次数
4. 采用适合的存储结构(哈希表)
为了高效率,本项目使用 链地址法哈希表(Hash Table + Linked List)。
5. 输出词频统计结果
如:
the : 135 and : 87 hello : 12 computer : 7
支持按字典序或出现次数排序(本项目提供二者示例)。
6. 详细注释,适合教学
每段代码必须含有详细注释,便于课堂讲解。
7. 可扩展性强
如:
- 忽略停用词(stop words)
- 输出前 N 高频词
- 导出 JSON 文件
- 分析多文件
相关技术详细介绍
本项目涉及到多项关键 C 语言技术:
1. 字符分类与处理
使用:
isalpha()tolower()
用于识别英文单词。
2. 字符串缓冲区
为了组装单词,需要一个动态缓冲区或固定大小数组,用来读入单词。
3. 哈希表(Hash Table)
使用链地址法(每个桶是一条链表):
- 哈希函数采用 BKDR Hash
- 冲突由链表解决
- 插入效果好,查询时间 O(1)
4. 动态内存管理
包括:
- malloc
- realloc
- free
- strdup(复制字符串)
必须确保无内存泄漏。
5. 文件 IO
使用:
- fopen
- fgets / fgetc
- fclose
实现逐字符解析。
6. 排序(qsort)
为了按词频排序,需要使用 C 标准库排序函数。
实现思路详细介绍
完整算法步骤如下:
步骤 1:读取文件
使用 fgetc() 每次读取一个字符。
步骤 2:识别单词
我们维护一个字符缓冲区 wordbuf:
如果字符是字母,追加到缓冲区
如果字符不是字母:
- 当前缓冲区非空 → 说明得到了一个完整单词
- 将单词转换为小写
- 将单词插入哈希表
- 清空缓冲区
步骤 3:哈希表插入逻辑
插入过程:
- 根据哈希计算桶 index
- 在桶的链表中查找单词是否存在
- 若已存在:count++
- 若不存在:创建新节点,加入链表头部
步骤 4:统计结束后输出所有词频
遍历哈希表,将所有节点存入数组,使用 qsort 排序。
步骤 5:按字典序或词频排序并输出
提供两种排序方式:
- 字典序(lexicographical)
- 按出现次数降序
完整实现代码
/************************************************************
* C语言实现英文词频统计(Word Frequency Count)
* 结构:哈希表 + 链地址法
* 文件:word_count.c
************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define HASH_SIZE 10007 // 哈希表大小(质数效果更好)
#define WORD_MAX_LEN 256 // 单词最大长度
/************************************************************
* 单词节点结构体:用于链表存储哈希冲突项
************************************************************/
typedef struct WordNode {
char* word; // 单词
int count; // 出现次数
struct WordNode* next; // 下一个节点
} WordNode;
/************************************************************
* 哈希表(链地址法)
************************************************************/
WordNode* hashtable[HASH_SIZE];
/************************************************************
* BKDR Hash 哈希函数
************************************************************/
unsigned int BKDRHash(const char* str)
{
unsigned int seed = 131;
unsigned int hash = 0;
while (*str) {
hash = hash * seed + (*str++);
}
return hash % HASH_SIZE;
}
/************************************************************
* 向哈希表插入一个单词(若存在则 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;
}
/************************************************************
* 从文件读取单词并统计
************************************************************/
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);
}
/************************************************************
* 排序结构
************************************************************/
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);
}
/************************************************************
* 主函数:接受文件名并统计
************************************************************/
int main()
{
char filename[256];
printf("请输入英文文本文件名:");
scanf("%s", filename);
count_words(filename);
printf("\n--- 按词频排序 ---\n");
print_words(1);
printf("\n--- 按字典序排序 ---\n");
print_words(0);
return 0;
}
代码详细解读
1. WordNode 结构体
用于存储单词和出现次数,链表解决哈希冲突。
2. hashtable 数组
固定大小 10007,每项是一个链表头指针。
3. BKDRHash
经典哈希函数:
- 冲突概率低
- 性能高
4. insert_word()
作用:
- 查找单词是否已存在
- 存在 → 次数 +1
- 不存在 → 创建新节点
5. count_words()
作用:
- 按字符读取文件
- 遇到字母 → 缓冲区存储
- 遇到非字母 → 形成一个单词并插入哈希表
- 自动转小写
6. 排序函数 cmp_count 和 cmp_alpha
支持 2 种排序:
- 按出现次数
- 按字典序
7. print_words()
将哈希表内容复制到数组,排序后输出。
8. main()
询问文件名 → 调用统计函数 → 打印排序结果。
项目详细总结
本项目实现了一个完整的英文词频统计系统,具有:
1. 结构清晰
采用:
- 哈希表
- 链地址法
- 文件流读取
- 自定义排序
2. 性能高
哈希查找 O(1),读取与插入速度极快。
3. 扩展性强
可以轻松加入:
- 停用词过滤
- 输出前 N 高频词
- 支持多文件处理
- 生成可视化统计结果
4. 工程意义大
是学习:
- 文本处理
- NLP
- 搜索引擎构建
- 哈希表
- 文件解析
- C 结构体封装
的基础项目。
项目常见问题与解答
1. 为什么不用数组存储所有单词?
因为英文单词数量未知,数组不适合动态增长。
2. 为什么使用哈希表?
查询快,冲突处理简单,是词频统计中最常见的数据结构。
3. 是否会有内存泄漏?
本实现未释放所有 WordNode(结束后可补充 destroy 函数)。如果你需要,我可以加入完整的内存释放模块。
4. 如何处理停用词(the、and...)?
可在插入前判断是否为黑名单词。
5. 如何统计一段很长的文本?
当前设计非常适合大文件处理,速度远超数组或线性查找。
扩展方向与性能优化
1. 加入停用词过滤(Stop Words):可以大幅提升分析质量。
2. 使用更快的哈希函数(MurmurHash):让哈希分布更均匀。
3. 多线程处理(分段统计 + 合并哈希表):适合大文本(如小说、日志)。
4. 使用 AVL / 红黑树 实现词频统计:天然有序,不需要qsort。
5. 导出 JSON 格式词频结果:接口友好,可用于前端可视化。
到此这篇关于C语言实现英文词频统计功能(附带源码)的文章就介绍到这了,更多相关C语言英文词频统计内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!


最新评论