楼主: GMIN123
516 0

[其他] C语言实现英文词频统计(附带源码) [推广有奖]

  • 0关注
  • 0粉丝

等待验证会员

学前班

40%

还不是VIP/贵宾

-

威望
0
论坛币
0 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
20 点
帖子
1
精华
0
在线时间
0 小时
注册时间
2018-3-26
最后登录
2018-3-26

楼主
GMIN123 发表于 2025-12-10 11:57:31 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

一、项目背景说明

词频统计(Word Frequency Count)是自然语言处理(NLP)和文本分析中的一项基础且关键的技术。其基本原理是对一段英文文本进行扫描,提取出所有由字母组成的单词,并统计每个单词在文本中出现的次数。

该技术广泛应用于多个重要领域:

  • 搜索引擎中的倒排索引构建:在网页内容分析过程中,需统计关键词的出现频率,以建立高效的倒排索引(Inverted Index),从而加快搜索响应速度。实现一个英文词频统计工具,实际上就完成了这一流程的基础模块。
  • 文本挖掘与特征提取:包括以下常见模型:
    • TF(Term Frequency)
    • TF-IDF(信息检索的核心算法)
    • Bag-of-Words 模型(词袋模型)
    • 语言模型(Language Model)
    这些方法均依赖于准确的词频数据作为输入。
  • 文档主题识别与写作风格分析:通过高频词汇判断文章所属主题或作者的语言习惯;同时可检测高频词是否为停用词(Stop Words),辅助去噪处理。
  • 嵌入式系统中的文本处理:在资源受限或性能要求极高的环境中(如C语言环境),无法使用Python等高级语言,因此需要基于C语言开发高效、低开销的文本分析组件。
  • 日志文件分析与服务器监控:可用于统计访问日志中各类信息的出现频率,例如:
    • IP地址访问次数
    • 请求URL的调用频次
    • 特定错误码连续出现的情况
    其底层机制本质上仍是字符串解析结合词频统计。

由此可见,利用C语言实现一个功能完整、运行高效且具备扩展能力的英文词频统计程序,对于掌握文本处理的整体逻辑具有重要意义。

二、项目需求说明

本项目旨在开发一个功能完善的英文词频统计工具,具体要求如下:

  1. 支持多种输入方式
    • 从文件读取文本内容
    • 通过标准输入(stdin)接收数据
    • 程序内嵌字符串(示例中采用文件读取方式)
  2. the : 135 and : 87 hello : 12 computer : 7
  3. 自动识别有效单词,满足以下条件:
    • 仅包含A–Z或a–z范围内的字母
    • 不区分大小写(如“Hello”与“hello”视为同一单词)
    • 忽略数字、标点符号及其他非字母字符
    • 支持长单词处理(例如:"internationalization")
  4. 准确记录每个单词的出现次数,需保存:
    • 单词本身的内容
    • 对应的出现频次
  5. 采用高效的存储结构:为提升性能,选用链地址法实现的哈希表(Hash Table + Linked List)来管理单词及其频次。
  6. 输出统计结果,并支持两种排序方式:
    • 按字典序排列
    • 按出现次数降序排列
    示例代码将提供两种输出模式的实现。
  7. 代码注释详尽,适合教学讲解:每段核心代码均配有清晰注释,便于课堂演示和学生理解。
  8. 具备良好的可扩展性,未来可轻松添加如下功能:
    • 过滤停用词(stop words)
    • 输出前N个最高频词
    • 导出结果为JSON格式文件
    • 支持多文件批量分析

三、关键技术点说明

该项目涉及多项核心C语言编程技术:

  1. 字符分类与处理:借助标准库函数判断字符类型,确保只提取合法字母。
    isalpha()
    tolower()
  2. 字符串缓冲区设计:使用固定大小数组或动态缓冲机制暂存正在读取的字符,用于拼接完整单词。
  3. 哈希表结构实现:采用链地址法解决冲突,每个桶对应一条链表。
    • 哈希函数选择经典的BKDR Hash算法
    • 插入效率高,平均查询时间复杂度接近O(1)
  4. 动态内存管理:合理使用以下函数:
    • malloc:分配初始内存
    • realloc:扩容缓冲区
    • free:释放不再使用的内存
    • strdup:复制字符串到新内存空间
    必须保证无内存泄漏。
  5. 文件IO操作:通过以下标准库函数完成文本读取:
    • fopen:打开文件
    • fgetc 或 fgets:逐字符或逐行读取
    • fclose:关闭文件流
  6. 排序功能实现:利用C标准库中的qsort函数对最终结果进行排序,支持按词频或字母顺序排列。

四、实现思路解析

整个程序的执行流程可分为以下几个步骤:

  1. 读取输入文件: 使用逐字符读取方式处理文本内容。
    fgetc()
  2. 识别并提取单词: 维护一个字符缓冲区用于临时存储当前单词。
    wordbuf
    • 若当前字符为字母,则追加至缓冲区
    • 若为非字母字符:
      • 若缓冲区非空,表示已形成一个完整单词
      • 将该单词转为小写
      • 插入哈希表
      • 清空缓冲区,准备下一个单词
  3. 哈希表插入逻辑
    • 根据BKDR哈希函数计算目标桶索引
    • 遍历对应链表,查找是否存在相同单词
    • 若存在 → 频次+1
    • 若不存在 → 创建新节点并插入链表头部
  4. 汇总输出结果: 将哈希表中所有节点复制到数组中,便于后续排序处理。
  5. 排序并展示结果: 提供两种输出模式:
    • 按字典序(lexicographical order)升序排列
    • 按出现次数降序排列
    利用qsort配合不同的比较函数实现。

五、完整代码实现

/************************************************************
 * 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. 哈希表数组声明:定义大小为10007的指针数组(素数尺寸减少冲突),每个元素为链表头指针。
  3. BKDRHash 函数:一种高效、低碰撞率的字符串哈希算法,适用于英文单词场景。
  4. insert_word() 函数:负责将单词插入哈希表,检查是否已存在,若存在则计数加一,否则创建新节点。
  5. count_words() 函数:主文本解析函数,按字符读取文件内容,利用缓冲区识别单词,完成小写转换后调用插入函数。
  6. cmp_count 与 cmp_alpha 排序函数:分别用于按频次降序和按字母升序排序,供qsort调用。
  7. print_words() 函数:将散落在哈希表各桶中的节点收集到数组中,执行排序后统一输出结果。
  8. main() 函数流程:提示用户输入文件名,调用统计函数处理文本,最后打印排序后的词频列表。

七、项目总结

本项目通过C语言实现了一个高性能、结构清晰的英文词频统计器,涵盖了文本解析、字符处理、哈希表设计、内存管理及排序输出等多项核心技术。不仅实现了基础功能,还具备良好的可维护性和扩展潜力,适合作为教学案例或嵌入式文本分析模块的基础原型。

通过对该程序的理解与实践,开发者能够深入掌握C语言在实际工程问题中的应用方式,尤其在资源受限环境下如何平衡效率与功能的设计思路。

本项目构建了一个功能完整的英文词频统计系统,具备以下特点:

一、结构设计清晰

系统采用多种高效技术实现核心功能:

  • 哈希表
  • 链地址法解决冲突
  • 通过文件流逐行读取文本
  • 支持自定义排序规则

二、运行性能优异

利用哈希查找的时间复杂度为 O(1),在单词插入与检索过程中表现出极高的处理速度。

the : 135 and : 87 hello : 12 computer : 7

三、良好的可扩展性

系统架构支持后续功能拓展,例如:

  • 增加停用词过滤机制
  • 输出出现频率最高的前 N 个词汇
  • 支持同时处理多个输入文件
  • 生成可视化图表或数据报告

四、具有实际工程价值

该项目是深入理解以下技术方向的良好实践基础:

  • 文本预处理
  • 自然语言处理(NLP)入门
  • 搜索引擎索引构建原理
  • 哈希表的应用与优化
  • 文件解析流程
  • C语言中结构体的封装与管理

五、常见问题解答

  1. 为何不使用数组存储所有单词?
    由于英文文本中可能出现的单词数量不确定,数组长度固定,难以动态扩展,因此不适合用于此类场景。
  2. 为何选择哈希表作为主要数据结构?
    哈希表具备快速查询能力,且冲突处理机制成熟,是词频统计中最常用的数据结构之一。
  3. 是否存在内存泄漏风险?
    当前版本未包含完整的内存释放逻辑(如 WordNode 的销毁),但可在程序结束时补充 destroy 函数以回收资源。可根据需要添加完整内存管理模块。
  4. 如何实现停用词过滤(如 the、and 等)?
    可在单词插入哈希表前进行判断,若属于预设的停用词列表,则直接跳过。
  5. 能否处理超长文本?
    该设计专为大文件处理优化,相比数组或线性查找方式,在处理大规模文本时性能优势明显。
    isalpha()

六、未来扩展与性能优化建议

  1. 引入停用词过滤机制(Stop Words)
    有效去除无意义高频词,显著提升分析结果的质量。
  2. 采用更高效的哈希函数(如 MurmurHash)
    改善哈希值分布均匀性,降低冲突概率,提高整体效率。
  3. 支持多线程并行处理
    将大文件分段,各线程独立统计后合并哈希表,适用于小说、日志等大型文本处理场景。
  4. 使用 AVL 树或红黑树替代哈希表
    这些结构本身有序,可省去额外的 qsort 排序步骤,适合对输出顺序有要求的情况。
  5. 导出 JSON 格式的统计结果
    提供标准化接口,便于前端调用并实现数据可视化展示。
    tolower()
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:C语言 Internation Frequency graphical filename
相关内容:C语言源码实现

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注jltj
拉您入交流群
GMT+8, 2025-12-19 22:04