c语言课设
单词管理系统设计文档
一、项目简介
本单词管理系统基于标准C语言开发,为控制台字符界面程序。系统主要用于实现英语单词的信息化管理,支持单词录入、查询、修改、删除、复习、保存、读取、统计、完整功能。程序利用结构体存储单词数据,通过文件读写实现数据持久化,关闭程序后数据不丢失。
本项目旨在巩固C语言结构化编程、结构体、数组、文件操作、循环与分支逻辑等核心知识点,适合高校C语言课程设计实训使用,系统结构清晰、代码规范、功能完整、运行稳定。
二、开发环境与技术栈
•开发语言:标准C语言(C89/C99)
•开发工具:Visual Studio Code
•运行环境:Windows 系统控制台
•核心技术点:链表结、构体数组、文件读写(txt本地持久化)、动态数据管理、分支与循环菜单、字符串处理、数据遍历匹配
三、系统需求分析
3.1 功能需求
本单词管理系统需满足以下核心功能:
•单词信息添加:用户可批量或单个录入单词、释义、例句等信息
•单词查询:支持按英文单词精准查询对应释义与例句
•单词修改:对已存在的单词信息进行更新修改
•单词删除:删除指定单词的整条记录
•单词复习:随机展示单词,实现简易背单词功能
•数据统计:统计当前系统存储的单词总数
•文件保存:将所有单词数据保存到本地txt文件
•文件读取:程序启动自动加载本地单词数据
•菜单导航:交互式中文菜单,操作简洁易懂
•单词复习:交互式的对单词进行中译英或者英译中的复习
3.2 性能需求
•程序运行稳定,无内存泄漏、无崩溃报错
•数据读写快速准确,查询、增删改操作响应即时
•支持多次连续操作,容错性强,非法输入可规避
3.3 安全需求
•文件读写安全,不会乱码、不会丢失已有数据
•查询、删除时对空数据进行判断,防止程序异常
四、系统总体设计
4.1 系统总体架构
本系统采用结构化模块化设计,整体分为:数据层、功能逻辑层、交互界面层。
•界面层:控制台菜单打印、用户输入交互
•逻辑层:增删改查、复习等业务功能
•数据层:结构体数组缓存数据,txt文件持久化存储
程序运行流程:启动加载文件数据 → 显示主菜单 → 根据用户选择调用对应功能模块 → 操作完成后自动保存数据 → 返回菜单,支持循环操作。
4.2 数据结构设计
使用结构体存储每条单词的完整信息,核心结构体定义如下:
c // 单词结构体 struct Word { char eng[50];// 英文单词 char chn[100];// 中文释义 char sentence[200]; // 例句 }; struct Word word[1000]; // 单词数组 int count = 0;// 当前单词数量 |
该结构可完整存储单词、释义、例句,满足课程设计数据存储需求,最大支持1000个单词存储。(在declarations.h中)
4.3 文件存储设计
系统采用普通txt文本文件存储数据,以换行分隔每条单词记录。程序启动读取文件内容到结构体数组,每次操作结束自动写入文件,保证数据持久化。
五、系统详细功能模块设计
5.1 主菜单模块
程序入口模块,负责打印系统功能菜单,接收用户数字选择,通过switch分支跳转对应功能函数,实现系统循环运行,输入错误时具备容错处理。
5.2 单词添加模块
用户输入单词、释义、例句,系统先遍历已有单词,判断是否重复。若不存在则新增单词,单词数量自增,完成后自动保存至文件。该模块保证数据不重复、录入规范。
5.3 单词查询模块
接收用户输入的英文单词,遍历结构体数组进行字符串匹配,匹配成功则输出单词释义与例句;匹配失败提示“未查询到该单词”。支持精准查询。
5.4 单词修改模块
输入待修改单词,遍历查找对应记录,找到后重新录入新的释义和例句,覆盖原有数据,修改完成自动保存文件,实现数据更新功能。
5.5 单词删除模块
输入目标单词,遍历匹配成功后,通过数组前移覆盖的方式删除当前单词,单词总数减一,删除后重新保存文件,完成数据删除。
5.6 单词复习模块
利用随机数随机抽取已存储的单词,展示中文释义,让用户回忆英文单词,实现简易背单词复习功能,提升系统实用性。
5.7 数据统计模块
直接读取全局单词数量变量,输出当前系统中存储的单词总数量,方便用户查看学习积累量。
5.8 文件读写模块
•读文件:程序启动自动打开word.txt,循环读取所有单词数据存入结构体数组
•写文件:每次增删改操作后重新覆盖写入所有数据,保证本地文件与程序数据同步
六、系统核心流程设计
1.程序启动,初始化变量,读取本地单词文件
2.加载历史数据,赋值给结构体数组
3.打印主菜单,等待用户功能选择
4.执行对应增删改查、复习、统计功能
5.操作完成自动保存数据到文件
6.返回菜单,循环执行,用户选择退出时关闭文件、结束程序
七、系统测试
7.1 功能测试
•添加测试:可正常新增单词,重复单词无法录入
•查询测试:存在单词可正常显示,无单词提示不存在
•修改测试:可成功更新单词释义与例句
•删除测试:指定单词可精准删除,数据同步更新
•复习测试:随机抽取单词正常展示
•文件测试:重启程序数据不丢失,持久化正常
7.2 异常测试
•空数据操作时程序不崩溃,给出友好提示
•非法菜单输入不会导致程序异常
•文件缺失时程序自动创建新文件,正常运行
7.3 测试结论
本系统所有功能运行正常,逻辑严谨,容错性良好,数据存储稳定,完全满足课程设计功能要求。
八、具体的代码
8.1 add.h文件
#ifndef ADD_H#define ADD_H#include"declarations.h"#include"utils.h"staticvoidchoice1(){struct word Word_next;if (count < 100000){printf("请输入要添加的单词:\n");scanf("%199s", Word_next.english);printf("请输入单词的翻译:\n");scanf("%199s", Word_next.chinese);printf("添加成功!\n");count++;current->next = (struct word*)malloc(sizeof(struct word)); // 为下一个单词分配内存current = current->next; // 移动当前指针到下一个单词*current = Word_next; // 将新单词的数据复制到当前节点}else{printf("单词已满!\n");}fanhui();qingping();return;}#endif
8.2 declarations.h文件
// declarations.h#ifndef DECLARATIONS_H#define DECLARATIONS_H#include#include#include#include#ifdef _WIN32#include#endif// 全局变量声明(定义在 main.c)extern int count;struct word{char chinese[200];char english[200];struct word *next;};//extern struct word Word[100000];extern struct word a;extern struct word *head; // 定义一个指向链表头的指针extern struct word *current; // 定义一个指向当前单词的指针//extern struct word *tail; // 定义一个指向链表尾的指针// 只声明定义在 save.c 中的函数voidsave();voidjiazai();voidchoice6();#endif
8.3 delete.h文件
#ifndef DELETE_H#define DELETE_H#include"declarations.h"#include"utils.h"staticvoidchoice2(){struct word *a=head;if(count==0){printf("词库为空");fanhui();return;}printf("请输入要删除的单词的英文:\n");char english[200];scanf("%199s", english);int i, fuhao = 1;for (i = 0; i < count; i++){if (strcmp(a->english, english) == 0){a = a->next;fuhao=0;break;}}if (fuhao){printf("没有此单词!\n");}if (i < count){a->next = a->next->next; // 跳过要删除的节点count--;printf("删除成功!\n");}fanhui();qingping();return;}#endif
8.4 list.h文件
#ifndef LIST_H#define LIST_H#include"declarations.h"#include"utils.h"staticvoidchoice5(){if(count==0){printf("词库为空");fanhui();return;}struct word *a=head;printf("所有单词如下:\n");for (int i = 0; i < count; i++){printf("单词的英文是:%s\t", a->english);printf("单词的中文是:%s\n", a->chinese);a = a->next;if ((i + 1) % 10 == 0 && i != count - 1){printf("--- 按任意键继续 ---\n");fanhui(); // 使用你已有的跨平台等待函数qingping();printf("所有单词如下(续):\n"); // 重新打印标题}}printf("--- 所有单词已列出 ---\n");fanhui();qingping();return;}#endif
8.5 main.c文件
#include"declarations.h"// 全局变量定义int count = 0;struct word *head = NULL;struct word *current = NULL;//struct word Word[100000];// 包含所有功能头文件(因为 main 需要调用它们)#include"add.h"#include"delete.h"#include"modify.h"#include"query.h"#include"list.h"#include"quiz.h"#include"utils.h"intmain(){jiazai();// 设置UTF-8编码支持#ifdef _WIN32system("chcp 65001 > nul"); // Windows下设置控制台为UTF-8模式#endifjiazai(); // 加载数据文件中的单词到内存while(1){qingping();printf("欢迎使用单词管理系统\n");printf("1. 添加单词\n");printf("2. 删除单词\n");printf("3. 修改单词\n");printf("4. 查询单词\n");printf("5. 列出所有单词\n");printf("6. 检验单词\n");printf("7. 退出系统\n");printf("请选择:");int choice;scanf("%d", &choice);qingping();switch(choice){case 1: {choice1();break;}case 2: {choice2();break;}case 3: {choice3();break;}case 4: {choice4();break;}case 5: {choice5();break;}case 6: {choice7();break;}case 7: {choice6();break;}default: {printf("无效的选择!\n");fanhui();break;}}}return 0;}
8.6 modify.h文件
#ifndef MODIFY_H#define MODIFY_H#include"declarations.h"#include"utils.h"staticvoidchoice3(){if(count==0){printf("词库为空");fanhui();return;}struct word *Worda=head;printf("请输入要修改的英文单词:(请输入所修改单词的英文进行修改)\n");char xiugai[200];scanf("%199s", xiugai);printf("要修改的是中文还是英文(输入1修改中文,输入2修改英文):\n");int choice;scanf("%d", &choice);switch (choice){case 1:{int biaozhi = 1;for (int i = 0; i < count; i++){if (strcmp(Worda->english, xiugai) == 0){printf("请输入修改后的单词中文:\n");scanf("%199s", Worda->chinese);printf("修改成功!\n");biaozhi = 0;break;}Worda = Worda->next;}if (biaozhi){printf("没有此单词!\n");}break;}case 2:{int biaozhi = 1;for (int i = 0; i < count; i++){if (strcmp(Worda->english, xiugai) == 0){printf("请输入修改后的单词英文:\n");scanf("%199s", Worda->english);printf("修改成功!\n");biaozhi = 0;break;}Worda = Worda->next;}if (biaozhi){printf("没有此单词!\n");}break;}default:{printf("无效的选择!\n");break;}}fanhui();qingping();return;}#endif
8.7 query.h文件
#ifndef QUERY_H#define QUERY_H#include"declarations.h"#include"utils.h"staticvoidchoice4(){if(count==0){printf("词库为空");fanhui();return;}struct word *Worda=head;printf("请输入要查询的单词的中文:\n");char target[200];scanf("%199s", target);int i, fuhao = 1;for (i = 0; i < count; i++){if (strcmp(Worda->chinese, target) == 0){printf("单词的英文是:%s\n", Worda->english);printf("单词的中文是:%s\n", Worda->chinese);fuhao = 0;break;}Worda = Worda->next;}if (fuhao){printf("没有此单词!\n");}fanhui();qingping();return;}#endif
8.8 quiz.h文件
#ifndef QUIZ_H#define QUIZ_H#include"declarations.h"#include"utils.h"#include#include#includestaticvoidchoice7(){// 1. 词库为空检查if (count == 0) {printf("词库为空,请先添加单词!\n");fanhui();return;}// 2. 获取用户输入的测试数量,并进行边界检查printf("请输入要检验的单词的个数:\n");int n;if (scanf("%d", &n) != 1 || n <= 0) {printf("输入无效!\n");fanhui();return;}// 如果请求的数量大于词库总数,则最多测试词库总数if (n > count) {printf("词库中仅有 %d 个单词,将测试全部单词。\n", count);n = count;}// 3. 询问测试模式int a;printf("中译英还是英译中?(输入1: 英译中 / 0: 中译英):\n");scanf("%d", &a);// 4. 将链表节点指针复制到数组中(以空间换时间,方便随机访问)struct word **word_array = (struct word **)malloc(count * sizeof(struct word *));if (word_array == NULL) {printf("内存分配失败!\n");fanhui();return;}struct word *temp = head;for (int i = 0; i < count; i++) {word_array[i] = temp;temp = temp->next;}// 5. 创建标记数组,防止在同一次测试中抽到重复的单词int *visited = (int *)calloc(count, sizeof(int)); // 初始化为0if (visited == NULL) {printf("内存分配失败!\n");free(word_array);fanhui();return;}int total_questions = n; // 答题总数int correct_count = 0; // 答对数量int questions_asked = 0; // 实际已提问数量printf("\n--- 开始测试 ---\n");while (questions_asked < n) {// 生成随机索引int index = rand() % count;// 如果该单词已经被抽过,则跳过,重新生成随机数if (visited[index] == 1) {continue;}// 标记该单词已被抽取visited[index] = 1;struct word *Worda = word_array[index];questions_asked++;char answer[200];if (a == 0) {// 中译英printf("[%d/%d] 请输入 \"%s\" 的英文:\n", questions_asked, n, Worda->chinese);scanf("%199s", answer);if (strcmp(answer, Worda->english) == 0) {printf("✅ 回答正确!\n\n");correct_count++;} else {printf("❌ 回答错误!正确答案是:%s\n\n", Worda->english);}} else if (a == 1) {// 英译中printf("[%d/%d] 请输入 \"%s\" 的中文:\n", questions_asked, n, Worda->english);scanf("%199s", answer);if (strcmp(answer, Worda->chinese) == 0) {printf("回答正确!\n\n");correct_count++;} else {printf("回答错误!正确答案是:%s\n\n", Worda->chinese);}} else {printf("输入的模式无效,跳过此题。\n\n");}}// 6. 打印测试结果printf("--- 答题结束!---\n");printf("总题数:%d\n", total_questions);printf("答对题数:%d\n", correct_count);printf("正确率:%.2f%%\n", (double)correct_count / total_questions * 100.0);// 7. 释放动态分配的内存,防止内存泄漏free(word_array);free(visited);fanhui();qingping();}#endif
8.9 save.c文件
#include"declarations.h"#include"utils.h"#include#include#include// 全局二维数组,用于保存时按首字母分类暂存单词struct word woe[26][1000];voidchoice6(){printf("退出系统!\n");printf("欢迎下次再来!\n");save();exit(0);}voidsave(){FILE *fp = fopen("data.txt", "w");if (fp == NULL) {printf("无法打开文件!请检查路径或权限。\n");return;}int count_save[26] = {0};memset(woe, 0, sizeof(woe)); // 清空全局数组防止残留struct word *Worda = head;while (Worda != NULL) {char first_char = Worda->english[0];int index = -1;if (first_char >= 'a' && first_char <= 'z') {index = first_char - 'a';} else if (first_char >= 'A' && first_char <= 'Z') {index = first_char - 'A';}if (index != -1 && count_save[index] < 1000) {strcpy(woe[index][count_save[index]].english, Worda->english);strcpy(woe[index][count_save[index]].chinese, Worda->chinese);count_save[index]++;}Worda = Worda->next; // 移动指针}for (int i = 0; i < 26; i++) {for (int j = 0; j < count_save[i]; j++) {fprintf(fp, "%s\t%s\n", woe[i][j].english, woe[i][j].chinese);}}fclose(fp);printf("数据已成功保存到 data.txt\n");}voidjiazai(){FILE *fp = fopen("data.txt", "r");if (fp == NULL) {printf("数据文件不存在,将创建新文件。\n");count = 0;head = NULL; // 确保头指针为空return;}count = 0;head = NULL; // 重置链表头指针struct word *tail = NULL; // 尾指针,用于高效地将新节点插入链表尾部char temp_eng[200], temp_ch[200];// 循环读取文件,每次读取一行(英文和中文)while (fscanf(fp, "%199s\t%199s", temp_eng, temp_ch) == 2) {// 1. 动态分配一个新的链表节点struct word *new_node = (struct word *)malloc(sizeof(struct word));if (new_node == NULL) {printf("内存分配失败!\n");break;}// 2. 将读取到的数据复制到新节点中strcpy(new_node->english, temp_eng);strcpy(new_node->chinese, temp_ch);new_node->next = NULL;// 3. 将新节点挂载到链表尾部if (head == NULL) {head = new_node; // 如果是第一个节点,头指针指向它} else {tail->next = new_node; // 否则,让当前的尾节点指向新节点}tail = new_node; // 更新尾指针count++; // 单词总数加 1}fclose(fp);printf("已加载 %d 个单词到链表中\n", count);}
8.10 utils.h文件
#ifndef UTILS_H#define UTILS_H#include"declarations.h"staticvoidfanhui(){#ifdef _WIN32_getch();#elseint c;while ((c = getchar()) != '\n' && c != EOF); // 清空残留换行符getchar(); // 等待用户真正输入#endif}staticvoidqingping(){#ifdef _WIN32system("cls");#elsesystem("clear");#endif}#endif
九、系统优缺点分析
9.1 优点
•代码结构模块化,逻辑清晰,易于阅读、修改和扩展
•实现数据持久化,重启程序数据不丢失
•功能完整,覆盖单词管理全部常用功能
•界面简洁、操作简单,适合初学者使用
9.2 不足与改进方向
•目前为链表保存,理论上没有上限,但是实际判断的大小最高为100000
•可增加单词分类、错题本、学习进度记录功能
•可增加模糊查询功能,提升检索体验
十、项目总结
本次C语言单词管理系统课程设计,完整实现了单词的添加、查询、修改、删除、随机复习、数据统计、文件持久化存储等功能。开发过程熟练掌握了结构体数组应用、字符串处理、文件读写、模块化编程思想,加深了对C语言结构化程序设计的理解。
系统结构规范、功能完善、运行稳定,不仅完成了课程设计要求,也提升了程序逻辑设计、问题排查、功能迭代的开发能力,达到了本次课程设计的学习目标。
十一、仓库地址
Gitee:https://gitee.com/speech-is/word-mangement
夜雨聆风