【实训】“宅急送”订餐管理系统(程序设计综合能力实训)

👀樊梓慕:个人主页

 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》

🌝每一个不曾起舞的日子,都是对生命的辜负


前言

        大一小学期,我迎来了人生中的第一次实训,“宅急送”订餐管理系统是一个非常好的检验该阶段所学知识的实训项目,本篇文章我会围绕这一实训项目以及在实训中遇到的问题和收获与大家进行探讨,内容包括流程图函数调用关系图算法描述以及代码实现等等,可供大家进行参考,希望大家多多点赞收藏支持🔥


欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。

=========================================================================

GITEE相关代码:🌟fanfei_c的仓库🌟

=========================================================================


1.详细设计

1.1上班

1.1.1算法描述

        首先对套餐顺序表进行初始化,然后从文件中读取套餐信息,并将其存储到套餐顺序表中,提示用户套餐信息读取完成,而后对订单信息队列进行初始化,从文件中读取订单信息,并将其存储到订单信息队列中,同样提示用户订单信息读取完成,最后则是对图进行初始化,从文件中分别读取地点信息和地点间距离信息,并将其存储到图中,提示地址和距离信息读取完成,而后等待用户进行下一步操作。

1.1.2流程图


1.2订单管理

1.2.1算法描述

1.接收订单

        从用户输入获取订单信息,包括订餐人姓名、订单号和目的地等,并判断输入的订单信息是否合法,然后创建一个临时变量用来存储订单信息,最后将该临时变量入队。

2.根据订单号查询订单

        从用户输入获取订餐人姓名,遍历队列中的每个节点,直到找到订单号匹配的节点为止,如果找到匹配的订单号,返回该节点的指针,否则返回空指针。

3.根据订餐人姓名查询订单

        从用户输入获取订餐人姓名,遍历队列中的每个节点,直到找到订餐人姓名匹配的节点为止,如果找到匹配的订餐人姓名,返回 true,否则返回 false。

4.根据订单号查询并修改订单

        调用订单号查询函数,获取该订单地址,根据用户输入的信息修改相应的订单属性,同样在其中穿插判断订单信息的合法性,最后更新订单信息。

5.根据订单号查询并取消订单

        从用户输入获取订单号,调用订单号查询函数,获取该订单地址,判断该订单是否已派送,将该订单状态信息更新为取消。

1.2.2流程图


1.3派送订单

1.3.1算法描述

1.遍历未派送订单

        以队列的size大小界定循环次数,遍历该队列,当派送状态为未派送时输出订单信息即可。

2.逐个派送

        首先找到订单队列中最靠近队首的未派送订单,然后利用弗洛伊德算法计算最短路径并生成最短距离矩阵D得到最短距离,和最短路径矩阵Path,利用PrintShortedPath函数输出最短路径,设骑手速度为30,计算出送达时间并输出,将订单状态修改为已派送,最后提示用户派送成功。

1.3.2流程图


1.4数据维护

1.4.1算法描述

1.添加套餐

        调用函数 CheckSetList检测套餐列表容量是否已满,如果已满则扩容,通过用户输入获取套餐信息,包括套餐编号、套餐名称、套餐描述、套餐价格和套餐状态,将获取的套餐信息依次存储到顺序表的末尾,增加顺序表长度,提示用户添加成功。

2.删除套餐

        通过用户输入获取要删除的套餐的编号,遍历顺序表中的每个套餐,直到找到与输入的编号匹配的套餐为止,如果找到匹配的套餐,询问用户是否确定删除该套餐,并根据用户的选择进行相应操作,如果用户确定删除套餐,则将该套餐从顺序表中删除,并提示用户删除成功。

3.根据套餐编号修改套餐信息

        通过用户输入获取要修改的套餐的编号,然后遍历顺序表中的每个套餐,直到找到与输入的编号匹配的套餐为止,如果找到匹配的套餐,通过用户输入修改套餐的名称、描述、价格和状态,提示用户修改成功,如果未找到匹配的套餐,提示用户修改失败并声明原因。

4.根据套餐名称修改套餐信息

        通过用户输入获取要修改的套餐的名称,然后遍历顺序表中的每个套餐,直到找到与输入的名称匹配的套餐为止,如果找到匹配的套餐,通过用户输入修改套餐的名称、描述、价格和状态,提示用户修改成功,如果未找到匹配的套餐,提示用户修改失败并声明原因。

5.恢复套餐信息

        首先初始化顺序表,然后调用函数 LoadSetList加载文件中的套餐信息,并将其存储到顺序表中,提示用户恢复套餐成功,之后调用函数 TraverseSet 遍历并打印顺序表中的套餐信息。

1.4.2流程图


1.5统计

1.5.1算法描述

1.当天统计

        遍历订单队列,获取订单的时间信息,判断订单的时间是否为当天,如果是则进行统计,统计未派送数量、已派送数量、总套餐数和总金额,输出统计结果。

2.当月统计

        遍历订单队列,获取订单的时间信息,判断订单的月份是否为当月,如果是则进行统计,遍历套餐列表,匹配订单的套餐编号,统计未派送数量、已派送数量、套餐数和总金额,最终输出统计结果。

3.当周统计

        遍历订单队列,获取订单的时间信息,判断订单的日期是否在本周范围内,如果是则进行统计,遍历套餐列表,匹配订单的套餐编号,统计未派送数量、已派送数量、套餐数和总金额,最终输出统计结果。

4.按地址统计

        遍历订单队列,获取订单的地址信息,遍历地址数组,匹配订单的地址,遍历套餐列表,匹配订单的套餐编号,统计订单数量和总金额,最终输出统计结果。

1.5.2流程图


1.6下班

1.6.1算法描述

        将顺序表中的内容存储到文本文件中,然后销毁为顺序表开辟的动态内存空间,将队列中的内容存储到文本文件中,销毁为队列开辟的动态内存空间,提示用户信息储存完毕,等待用户退出程序。

1.6.2流程图


2.代码实现

=========================================================================

GITEE相关代码:🌟fanfei_c的仓库🌟

=========================================================================


3.函数调用关系

该图采用Mindmaster(亿图脑图)创作完成。


4.遇到的问题

1.fread与fwrite只能读取和写入二进制文件,不能读取文本文件(造成乱码的原因)

在实训初期,文件读取操作是比较棘手的难题,由于对于文件操作函数使用的不熟练,常常陷入读取文本出现乱码的问题,经过仔细研究文件操作函数,最终成功解决了这一问题。

为了以后方便学习,在套餐信息的读取和写入中我使用了二进制读取写入方式,而订单信息和图的信息我才用了文本读取写入方式。

二进制读取举例:

void Save_Set(SetList* s)//存储套餐信息文件
{FILE* pf = fopen("Menu_Info.txt", "w");//保存套餐信息到文件if (pf == NULL){perror("fopen Menu_Info");return;}int i = 0;for (i = 0; i < s->length; i++){fwrite(&s->setmea[i], sizeof(Meal), 1, pf);}
}
void LoadSetList(SetList* s)//加载套餐信息文件
{FILE* pf = fopen("Menu_Info.txt", "rb");if (pf == NULL){perror("LoadSetList");return;}Meal tmp = { 0 };while (fread(&tmp, sizeof(Meal), 1, pf)){CheckSetList(s);s->setmea[s->length] = tmp;s->length++;}printf("Menu_Info.txt加载成功!\n");fclose(pf);return;
}

 文本读取举例:

void Save_Book(Que* pq)// 存储订单信息文件
{FILE* pf = fopen("Book_Info.txt", "w");if (pf == NULL){perror("fopen Book_Info");return;}while (!QueueEmpty(pq)){Order_Info tmp = { 0 };tmp = QueueFront(pq);fprintf(pf, "%s ", tmp.Order_Num);fprintf(pf, "%s ", tmp.Menu_Num);fprintf(pf, "%d ", tmp.Num);fprintf(pf, "%s ", tmp.Orderer_Name);fprintf(pf, "%s ", tmp.Order_Tele);fprintf(pf, "%s ", tmp.Address);fprintf(pf, "%s ", tmp.Order_Time);fprintf(pf, "%d ", tmp.Deliver_State);fprintf(pf, "%d\n", tmp.timestamp);QueuePop(pq);}
}
void LoadBook(Que* pq)//加载订单信息
{FILE* pf = fopen("Book_Info.txt", "r");if (pf == NULL){perror("LoadBook");return;}char line[150];while (fgets(line, sizeof(line), pf)) // 逐行读取文件内容{Order_Info tmp = { 0 };sscanf(line, "%s %s %d %s %s %s %s %d %d", tmp.Order_Num, tmp.Menu_Num, &tmp.Num,tmp.Orderer_Name,tmp.Order_Tele,tmp.Address,tmp.Order_Time,&tmp.Deliver_State,&tmp.timestamp); // 解析行中的数据QueuePush(pq, tmp);}printf("Book_Info.txt加载成功!\n");fclose(pf);return;
}

 大家如果对文件操作相关知识有所欠缺,可以浏览下我之前写的博客->【C语言】文件操作🍎


2.使用feof()函数判断文件结束是不合适的

        在处理文件指针时,判断文件是否已经到达末尾的一种常见方法是使用feof()函数(feof()函数会在文件指针到达文件末尾时返回非零值,否则返回0)

        因此,在循环中使用while (!feof(pf))来判断文件是否到达末尾,看起来似乎是一种合理的做法。

        然而,实际上在循环中使用while (!feof(pf))可能会导致最后一次循环读取无效数据的问题。这是因为feof()函数的返回值只有在文件指针尝试读取文件末尾之后才会为真。也就是说,在feof()函数返回真之前,文件指针可能已经读取了文件末尾之后的数据,但循环仍然会继续执行一次。因此,在最后一次循环中,会读取到文件末尾之后的无效数据。

        为了避免这个问题,可以使用替代方案来处理文件指针。一种常见的替代方案是使用fgets()函数来逐行读取文件内容,并在循环中检查fgets()函数的返回值是否为NULL,以判断是否到达文件末尾。如果fgets()函数返回NULL,说明文件已经到达末尾,循环应该结束。这种方式可以确保最后一次循环不会读取无效数据。

        使用fgets()函数逐行读取文件内容,当fgets()函数返回NULL时,循环结束。这样确保了最后一次循环不会读取无效数据。

while (fgets(line, sizeof(line), pf))

{

    // 处理每一行的数据

    // ...

}

        总结起来,建议避免在循环中使用feof()函数来判断文件是否到达末尾,而是使用替代方案如fgets()函数来处理文件指针,以确保最后一次循环不会读取无效数据。

下面是feof函数的正确使用方法:

int main()
{FILE* file = fopen("data.txt", "r");if (file == NULL){perror("fopen");return 1;}char buffer[100];while (fgets(buffer, sizeof(buffer), file) != NULL){printf("%s", buffer);}if (feof(file))//将feof的判断放在循环末尾{printf("Reached end of file.\n");}else{printf("Error occurred while reading file.\n");}fclose(file);return 0;
}

        由于当fgets(buffer, sizeof(buffer), file)函数的返回值为NULL时,说明文件已经到达末尾,此时循环结束,再利用feof(file)做判断,如果此时feof(file)的返回值为真,证明文件确实到达了末尾,但如果feof(file)的返回值为假,那证明文件在读取的过程中发生了错误,需要进行检查。

        由此才是feof()函数的正确用法,这样的方法也更加严谨安全


3.跨文件结构体指针作为参数无法识别,必须使用struct +结构体名

        这个问题是在基本完成各个源文件后,合并在一起实现时出现的问题,即多文件形式引起的错误,我发现虽然在头文件中定义结构体:

typedef struct Queue

{

      …

}Queue;

        在函数形参中,参数格式为Queue* 即可,但我发现编译器会报错,当加上struct Queue*就不报错了,所以我推测跨文件结构体指针作为参数无法识别,必须适用struct Queue*的格式才行,下面是问题截图:

        当然,这只是我的推测,在请教学校老师后,老师给了我这个答案:可能是我所使用的编译器不支持省略struct的写法,如果有大佬发现我的说法是错误的话,希望可以帮我指正出来,谢谢! 


4.统计板块中的时间识别问题

        如何界定统计中的当日统计、当月统计和本周统计呢,我的思路是将订单结构体中新增一个结构体变量time_t timestamp,在日后需要识别订单时间时,利用localtime(&p->data.timestamp),就能拿到订单的时间了,否则还需要对时间字符串char Order_Time[25]进行转化识别,下面是localtime返回值tm结构体的成员,可作为参考:

        诚然这种思路可以实现统计的功能,但在本周统计上,仍然需要考虑该周跨月甚至跨年的问题,我感觉较为繁琐,不知道大家对于统计的算法有没有什么更好的思路可以分享的呢,欢迎大家在评论区多多交流🌝


5.需要读取的时间字符串中间存在空格

        订单信息中的下单时间,需求文档中给定的格式是2023-08-28 19:00:00,日期和时间中间存在一个空格,这就与其他信息间存在冲突,因为读取文件信息我采用的是sscanf函数,sscanf遇到空格即停止读取,这也是一段字符串中不同内容可以区分的特性,而时间字符串中存在一个空格,这就导致sscanf读取到2023-08-28即停止,19:00:00就被认为是下一个内容了

        我的解决方案是将时间的格式更改为2023-08-28/19:00:00,这样就解决了这一问题。

        但其实这是一种妥协,另一种方法则是将日期2023-08-29看作一个字符串,将时间14:53:30看作一个字符串,分别读取到不同的结构体变量中,当然如果大家有更好的方法,希望可以分享在评论区😁


总结

        本次实训极大的考验了我的代码能力,在程序设计前期,由于对文件操作知识掌握相对薄弱,对于二进制读取和文本读取还没有清晰的认识,导致在测试调试过程中常常出现乱码等问题,甚至在实训前期一度对自己产生怀疑,但好在翻过了文件操作这一座高山,其他的问题基本上就是轻舟已过万重山。

        为期两周的实训结束了,我交上了自己还算满意的答卷,大一小学期的这次实训让我收获满满,也希望各位读者在文章中我遇到的问题上可以给出指导或建议,谢谢大家!

=========================================================================

如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容

🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎

🌟~ 点赞收藏+关注 ~🌟

=========================================================================

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/126374.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LNMP架构搭建论坛

目录 一、LNMP简介&#xff1a; 二、LNMP搭建&#xff1a; 1.前提准备&#xff1a; 关闭防火墙和安全机制&#xff1a; 2.编译安装nginx&#xff1a; 3.编译安装mysql&#xff1a; 3.1 安装依赖环境&#xff1a; 3.2 创建mysql运行用户&#xff1a; 3.3 编译安装&#xff1a…

神经网络与强化学习:揭示AI的超能力

文章目录 神经网络&#xff1a;模拟人脑的工具强化学习&#xff1a;通过试错学习结合神经网络和强化学习价值网络策略网络结合训练 应用领域游戏机器人控制金融交易 未来趋势自动化和自主系统个性化和自适应系统跨学科研究 结论 &#x1f389;欢迎来到AIGC人工智能专栏~神经网络…

msvcp110.dll是什么意思与msvcp110.dll丢失的解决方法

电脑突然提示msvcp110.dll丢失&#xff0c;无法执行此代码。导致软件无法打开运行&#xff0c;这个怎么办呢&#xff1f;我在网上找了一天的资料&#xff0c;终于把这个问题彻底处理好&#xff0c;也弄清楚了msvcp110.dll丢失的原因及msvcp110.dll丢失修复方法&#xff1f;现在…

LDAP服务器如何重启

1、find / -name ldap 该命令只会从根路径下查看ldap文件夹 find / -name ldap2、该命令会从根路径/查看所有包含ldap路径的文件夹&#xff0c;会查询出所有&#xff0c;相当于全局查询 find / -name *ldap*2、启动OpenLADP 找到LDAP安装目录后&#xff0c;执行以下命令 #直…

pytorch搭建squeezenet网络的整套工程,及其转tensorrt进行cuda加速

本来&#xff0c;前辈们用caffe搭建了一个squeezenet的工程&#xff0c;用起来也还行&#xff0c;但考虑到caffe的停更后续转trt应用在工程上时可能会有版本的问题所以搭建了一个pytorch版本的。 以下的环境搭建不再细说&#xff0c;主要就是pyorch&#xff0c;其余的需要什么p…

ABAP BAPI_ACC_DOCUMENT_POST 中 EXTENSION1的用法

BAPI_ACC_DOCUMENT_POST 在过账会计凭证时候&#xff0c;经常会发现一些标准字段在参数中并没有 可以通过CMOD/SMOD增强出口--》ACBAPI01--》EXIT_SAPLACC4_001--》ZXACCU15 示例代码&#xff1a; DATA: wa_extension TYPE bapiextc,it_extension TYPE STANDARD TABLE OF ba…

OpenCV(二十一):椒盐噪声和高斯噪声的产生

目录 1.图像噪声介绍 2.椒盐噪声的产生 3.高斯噪声的产生 1.图像噪声介绍 噪声介绍 图像噪声是指在图像中存在的不期望的、随机的像素值变化&#xff0c;这些变化来源于多种因素。噪声可能导致图像细节模糊、失真或难以分辨。 以下是几种常见的图像噪声类型&#xff1a; 1…

javascritp如何判断是从刷新(重新加载)、正常打开(或链接打开)、还是从浏览器回退进入页面的

重点先下另外一个知识点&#xff1a; 当我们的Web站点采用主体页面的iframe导航各个子页面&#xff08;浏览器地址保持不变&#xff09;的情况&#xff0c;如果我们希望每次iframe中打开的新的子页面&#xff0c;也都能够像在不采用iframe的情况那样&#xff0c;后续能够在浏览…

Tailwind 练手项目

Tailwind 练手项目 用到的技巧 Tailwind CSS 速成 应该都提过了&#xff0c;我不记得这里有什么特别新的知识 整体完成图大概这样&#xff1a; 一个纯静态页面&#xff0c;没有做 JS 之类的特效&#xff0c;不过做了移动端适配&#xff0c;说实话我写到一半的时候改了不少………

易云维®医院后勤管理系统软件利用物联网智能网关帮助实现医院设备实现智能化、信息化管理

近年来&#xff0c;我国医院逐渐意识到医院设备信息化管理的重要性&#xff0c;逐步建立医院后勤管理系统软件&#xff0c;以提高信息化管理水平。该系统是利用数据库技术&#xff0c;为医院的中央空调、洁净空调、电梯、锅炉、医疗设备等建立电子档案&#xff0c;把设备监控、…

mysql 安全加固

PS&#xff1a;之前在做安全测试的时候&#xff0c;报告mysql有安全漏洞&#xff0c;于是研究了下如何修复&#xff0c;于是记录下来分享给大家 1.1修改mysql 存放位置 修复 1.停服务 service mysqld stop2.迁位置 2.1 新建迁移目录 mkdir /home/database2.2 迁移数据文件…

【业务功能篇96】微服务-springcloud-springboot-认证服务-登录注册功能-Auth2.0-分布式session

5.登录功能 通过最基础的登录操作来完成登录处理 登录页面处理 认证服务的处理 /*** 注册的方法* return*/PostMapping("/login")public String login(LoginVo loginVo , RedirectAttributes redirectAttributes){R r memberFeginService.login(loginVo);if(r.getC…

数据结构:线性表之-单向链表(无头)

目录 什么是单向链表 顺序表和链表的区别和联系 顺序表&#xff1a; 链表&#xff1a; 链表表示(单项)和实现 1.1 链表的概念及结构 1.2单链表(无头)的实现 所用文件 将有以下功能&#xff1a; 链表定义 创建新链表元素 尾插 头插 尾删 头删 查找-给一个节点的…

(二十二)大数据实战——Flume数据采集之故障转移案例实战

前言 本节内容我们完成Flume数据采集的故障转移案例&#xff0c;使用三台服务器&#xff0c;一台服务器负责采集nc数据&#xff0c;通过使用failover模式的Sink处理器完成监控数据的故障转移&#xff0c;使用Avro的方式完成flume之间采集数据的传输。整体架构如下&#xff1a;…

OpenCV 01(图像加载与显示)

一、机器视觉 现在说的机器视觉(Machine Vision)一般指计算机视觉(Computer Vision), 简单来说就是研究如何使机器看懂东西。就是是指用摄影机和电脑代替人眼对目标进行识别、跟踪和测量等机器视觉&#xff0c;并进一步做图形处理&#xff0c;使电脑处理成为更适合人眼观察或传…

VS编译.cu文件源文件无法打开matrix.h和mex.h问题

配置好cu和VS相关库文件后CUDA程序仍然报错&#xff1a;无法打开matrix.h和mex.h&#xff0c;解决办法&#xff1a; &#xff08;1&#xff09;这两个头文件是matlab中的&#xff0c;可能无法直接在VS中调用&#xff0c;可以通过添加外部依赖项的方法将matlab中的头文件的文件路…

1600*C. Maximum Set

解析&#xff1a; 尽可能的增大集合内的数&#xff0c;所以倍数要尽可能的小&#xff0c;所以让最小的数不断乘 2&#xff0c;即可找到最大的数量。 所以&#xff0c;每次计算 k log2&#xff08; y / x &#xff09;,这样可得出最小的 x&#xff0c;乘多少个 2&#xff0c;能…

VS Code断点调式Cesium

1.在VS Code中安装Debugger for Firefox插件 2.下载安Firefox Developer Edition 3. 创建launch.json 编辑并保存launch.json {// Use IntelliSense to learn about possible attributes.// Hover to view descriptions of existing attributes.// For more information, vis…

MJDK 如何实现压缩速率的 5 倍提升?

MJDK 是基于 OpenJDK 构建的美团 JDK 发行版。本文主要介绍 MJDK 是如何在保障 java.util.zip.* API 及压缩格式兼容性的前提下&#xff0c;实现压缩/解压缩速率提升 5-10 倍的效果。希望相关的经验能够帮助到更多的技术同学。 1 前言 数据压缩技术[1]因可有效降低数据存储及…

strerror函数

目录 strerror 函数介绍&#xff1a; 举例&#xff1a; 使用案例&#xff1a; 优化&#xff1a; perror&#xff1a; strerror 函数介绍&#xff1a; 函数声明&#xff1a; char * strerror ( int errnum );头 文 件&#xff1a;#include <string.h>返 回 值&a…