小组团队合作完成,在最初基础(即增删查改基础上再做更新)
设计一个学生消费管理系统,消费信息包括消费的时间,消费的种类以及消费的金额等,其中时间不重复,该系统能实现以下几个功能
- 消费信息录入功能:将所录入的数据储存在文件中;
- 消费信息统计功能:从文件中将信息读取,显示在显示器上;
- 消费信息查询功能:包括消费时间查询;
- 消费信息删除功能:按照消费时间进行删除操作
- 消费信息修改功能:按时间修改;
- 消费信息排序功能:按金额大小排序;
- 文件的保存:将数据写入后对文件的保存;
- 文件的加载:在访问数据时对文件的加载;
- 对系统的美化:调用颜色函数来美化系统
具体描述如下:
1消费信息录入功能
对消费信息进行管理,首先要把消费信息储存在文件里,因此,该系统应具备录入消费信息的功能。
关于c语言所对文件能进行的文件储存按照二进制或者asccii码的形式,在消费系统中包含消费时间,消费种类,消费金额,又因为消费信息为结构体,因此我们考虑用二进制形式进行储存,将文件打开关闭调用了fopen和fread函数,当我们输入完信息后我们将文件关闭,此时我们已经将输入的数据储存在了文件中,对文件的读写调用系统函数fread和fwrite,并判断文件是否结束,此时可以调用feof函数来进行判断。
生成数据文件,当我们从键盘键入数据的时候,将所有的数据建立成一个结构体链表,当我们发出键入结束的指令时,一次性得将这些结点信息写入数据文件中,这样就节省了由于来回读写文件所占用的时间,降低了程序的时间复杂度,本系统我们建立链表时采用的时头插法。
2消费信息统计功能
对我们存入文件的数据进行读取统计,这就需要调用fopen函数来打开文件,fread来读取文件中所储存的数据,然后用fclose来将文件关闭,此时便将数据完全读取了,然后执行循环,将我们所输入的每一条信息全部打印出来,这部分我们调用了简单的printf函数进行数据输出打印,直到文件读空时循环结束,停止打印,此时显示器上是所有我们输入的数据
我们定义一个指向Node
类型的指针head
作为参数。这个指针代表链表的头部,定义了一个整数变量count
并初始化为0,这个变量用于存储学生的消费总金额。这段代码遍历链表中的每个结点,并累加每个学生的消费金额,每当遍历到一个结点时,它将该结点的消费金额加到count
变量上,然后移动到下一个结点,一旦遍历完链表,它打印出消费的总金额count,同时我们调用了系统命令pause
,使得程序暂停并等待用户按键继续。这是一种常见的在Windows系统上暂停程序的方法
3消费信息查查询功能
我们采用在链表中查找具有特定日期的学生消费记录,函数名为findstudent,我们定义了一个Node的类型的指针head作为参数,该指针就指向链表的头部,同时我们定义了一个整数变量a并将其初始化为0,该变量用来计数与输入日期匹配的记录的数量,我们在定义结构体的时候定义了一个字符数组date,因为本系统是通过查找日期来实现查找消费记录功能,首先,提示用户输入要查找的消费的时间,然后接着使用scanf
函数从用户处获取输入的日期并存储在date
中,然后是一个while
循环,它遍历链表中的每个结点,直到找到与输入日期匹配的记录或到达链表的末尾,使用strcmp函数比较当前节点的日期与输入的日期。如果它们相同,则打印出该节点的所有信息并将a增加1。执行完这一部分后将指针移动到链表中的下一个结点,在循环结束后,检查是否找到了任何匹配的记录。如果没有找到匹配的记录,则打印一个消息告诉用户没有找到消费信息
4消费信息删除功能
我们定义了一个名为deletStudent
的函数,它的目的是从链表中删除具有特定日期的学生消费记录,它接受一个指向Node
类型的指针head
作为参数,这个指针代表链表的头部,定义了一个字符数组date
用于存储用户输入的日期,以及一个指向Node
的指针move
初始化为链表的头部,首先,它提示用户输入要删除的消费时间。然后,使用scanf
函数从用户处获取输入的日期并存储在date
变量中,并执行一个while
循环,它遍历链表中的每个节点,直到找到与输入日期匹配的记录或到达链表的末尾,在循环内部:
使用strcmp函数比较当前节点的下一个节点的日期与输入的日期,如果它们相同,则执行以下操作:
保存当前节点的下一个节点到变量lin。
将当前节点的next指针指向当前节点的下一个节点的下一个节点,从而跳过当前节点的下一个节点。
使用free函数释放已删除结点(即lin)的内存。
调用saveStudent(head)函数,对剩余数据进行保存
使用系统命令pause暂停程序。
使用系统命令cls清空控制台屏幕。
返回,结束函数。如果循环结束后仍未找到匹配的日期,则打印“未找到消费信息”。
最后,再次使用系统命令pause
和cls
来暂停程序和清空屏幕。这是在提示用户输入日期后进行的操作,但如果找到日期并成功删除后也会执行。这部分代码放在了循环外,因此只会在没有找到日期时执行
5消费信息修改功能
我们定义了一个名为modifyStudent
的函数,它的目的是修改链表中具有特定消费时间的学生的消费记录,这是一个名为 modifyStudent
的函数,是一个指向Node
类型的指针head
作为参数,这个指针代表链表的头部,在初始结构体中我们定义了一个字符数组date
用于存储用户输入的日期,以及一个指向Node
的指针move
初始化为链表的第二个节点(即头节点的下一个节点)。首先,它提示用户输入要修改的消费时间。然后,它使用scanf
函数从用户处获取输入的日期并存储在date
变量中,然后执行一个while
循环,它遍历链表中的每个结点,直到找到与输入日期匹配的记录或到达链表的末尾,在while循环内部:
使用strcmp函数比较当前节点的日期与输入的日期。如果它们相同,则执行以下操作:
提示用户输入新的消费种类和金额。
使用scanf函数从用户处获取新的消费种类和金额,并存储在节点的相应字段中。
打印“修改成功”。
调用saveStudent(head)函数,这是我们所定义的储存数据的函数
使用系统命令pause暂停程序。
使用系统命令cls清空控制台屏幕。
返回,结束函数。如果循环结束后仍未找到匹配的日期,则打印“未找到消费信息”
最后,再次使用系统命令pause
和cls
来暂停程序和清空屏幕。这是在提示用户输入日期后进行的操作,但如果找到日期并成功修改后也会执行。这部分代码放在了循环外,因此只会在没有找到日期时执行
6消费信息排序功能
这是一个名为sortStudent
的函数,它接受一个指向Node
类型的指针head
作为参数,表示链表的头部,我们定义了两个指向Node
的指针a
和b
。这是用于遍历链表并比较结点中的学生消费金额的双重循环,外层循环变量a
遍历链表,内层循环变量b
遍历与a
相邻的结点,如果当前结点a
的学生消费金额大于其相邻结点b
的学生消费金额,则交换这两个节点的学生信息。Student
是我们所定义的结构体,代表学生的信息(消费日期、消费种类、消费金额),在排序完成后,调用一个名为printStudent
的函数,将我们排序后的消费记录打印出来,这是为了验证排序结果并将其显示给用户,这段代码完成了对学生消费金额的排序,但未修改链表本身的结构
7文件的保存
我们定义了一个名为saveStudent
的函数,它的目的是将学生消费信息保存到一个文件中,它接受一个指向Node
类型的指针head
作为参数,表示链表的头部,使用fopen
函数打开一个名为Consumption.txt
的文件,以写入模式("w")打开,如果文件不存在,它会被创建,如果文件已存在,它的内容会被清空,使用一个指针move
从链表的第二个节点开始遍历整个链表,使用fwrite
函数将当前结点(即move
所指向的结点)中的学生信息写入文件,如果写入失败(例如由于磁盘空间不足或磁盘错误等一些问题),则会打印“保存失败”并返回,当执行完所有写入操作后,使用fclose
函数关闭文件
8文件的加载
我们定义了一个名为loadStudent
的函数,它的目的是从文件中加载学生消费信息并重新构建一个链表,这个函数接受一个指向Node
类型的指针head
作为参数,表示链表的头部,使用fopen
函数打开一个名为Consumption.txt
的文件,以只读模式("r")打开。为新结点分配内存,如果文件不存在,则打印一条消息并返回,这里使用了循环来读取文件中的每个学生的消费信息:在每次循环中:
使用fread函数从文件中读取一个学生的消费信息。
将当前节点(move所指向的节点)的下一个结点设置为新结点(fresh)
将move指针移动到新节点
为新结点分配内存。
在循环结束后,释放最后一结点的内存,使用fclose
函数关闭文件,使用系统命令cls
清空控制台屏幕,然后打印“读取成功”的消息,这是在提示用户输入日期后进行的操作,但如果找到日期并成功修改后也会执行。这部分代码放在了循环外,因此只会在没有找到日期时执行
9对系统的美化
我们定义了一个名为 color
的函数,该函数用于设置控制台文本的颜色,它接受一个类型为 short
的参数 x
,我们先检查 x
是否在 0 到 15 的范围内,控制台文本的颜色通常使用一个 16 色的调色板,其中 0 到 15 对应不同的颜色,当x
在允许的范围内,则使用 SetConsoleTextAttribute
函数来设置控制台文本的颜色,这个函数需要两个参数,如果 x
不在允许的范围内,仍然会尝试设置控制台文本颜色,但实际上这可能不会产生预期的效果,因为 x
可能超出了 SetConsoleTextAttribute
函数所支持的颜色范围。代码中存在一个小问题。当 x
不在 0 到 15 的范围内时,虽然调用了 SetConsoleTextAttribute
函数,但传递给它的参数 x
可能超出了有效颜色的范围,这可能导致不可预测的行为。为了避免这种情况,应该确保在所有情况下都只传递有效的颜色值 给 SetConsoleTextAttribute
函数
三、详细设计
void inputStudent(Node* head)
{
Node* move = head;
Node* fresh = (Node*)malloc(sizeof(Node));
fresh->next = NULL;
color(7);
printf("请输入消费消费的时间,种类,金额\n");
color(11);
scanf("%s%s%d", fresh->student.date, fresh->student.name, &fresh->student.price);
while (move->next != NULL)
{
move = move->next;
}
move->next = fresh;
saveStudent(head);
system("pause");
system("cls");
}
Node* move = head;:定义一个指针move并初始化为指向链表的头部(头结点)。
Node* fresh = (Node*)malloc(sizeof(Node));:动态分配一个新的节点,并初始化其next指针为NULL。printf("请输入消费消费的时间,种类,金额\n");:提示用户输入消费的时间、种类和金额。scanf("%s%s%d", fresh->student.date, fresh->student.name, &fresh->student.price);:从标准输入读取用户输入的时间、种类和金额,并存储在新的节点中。while (move->next != NULL):循环遍历链表,直到到达链表的末尾。move = move->next;:移动指针到链表的下一个节点。move->next = fresh;:在链表的末尾添加新的节点。
saveStudent(head);:调用一个名为saveStudent的函数,并传递头结点作为参数。这个函数是用于将链表中的数据保存到文件。system("pause");和system("cls");:这两个函数调用是系统命令,用于暂停程序执行和清空控制台窗口
void printStudent(Node* head)//将储存的信息全部打印出来
{
Node* move = head->next;
while (move != NULL)
{
color(7);
printf("时间:%s 种类:%s 金额:%d\n", move->student.date, move->student.name, move->student.price);
move = move->next;
}
system("pause");
system("cls");
}
Node* move = head->next;:定义一个指针move并初始化为指向链表的第二个节点。while (move != NULL):开始一个循环,只要指针move不为空(即链表还有节点),就继续循环。printf("时间:%s 种类:%s 金额:%d\n", move->student.date, move->student.name, move->student.price);:打印消费记录的时间、种类和金额。
move = move->next;:将指针move移动到链表的下一个节点。system("pause");:调用系统命令pause,暂停程序执行,等待用户按键。system("cls");:调用系统命令cls,清空控制台窗口。
countStudent(Node* head)函数这个函数的目的是计算并打印出链表中所有学生的消费总金额。1.初始化计数器:初始化一个变量count为0,用于累计消费总金额。2.遍历链表:同样使用while循环遍历链表,直到move指针变为NULL。在循环体中,将当前节点的消费金额加到count上。3.打印总金额:在遍历结束后,打印出消费的总金额。4.清空控制台:同样使用system("cls")来清空控制台。
void findStudent(Node* head)
{
Node* move = head->next;
char date[20];
color(7);
printf("请输入要查找的消费的时间:");
color(11);
scanf("%s", date);
while (move != NULL)
{
if (strcmp(date, move->student.date) == 0)
{
color(7);
printf("时间:%s 种类:%s 金额:%d\n", move->student.date, move->student.name, move->student.price);
system("pause");
system("cls");
return;}
move = move->next;
}color(7);
printf("未找到消费信息\n");
system("pause");
system("cls");}
Node* move = head->next;: 定义一个指针move,并初始化为指向链表的第二个节点。char date[20];: 定义一个字符数组date,用于存储用户输入的时间。printf("请输入要查找的消费的时间:"): 打印提示信息,要求用户输入要查找的消费时间。scanf("%s", date);: 从标准输入读取一个字符串,并存储在变量date中。while (move != NULL): 开始一个循环,只要指针move不为空(即链表还有节点),就继续循环。
if (strcmp(date, move->student.date) == 0): 使用strcmp函数比较用户输入的日期与当前节点的学生日期是否相同。如果相同,执行以下操作:
-
- 打印消费记录的时间、种类和金额。
- system("pause");: 调用系统命令pause,暂停程序执行,等待用户按键。
- system("cls");: 调用系统命令cls,清空控制台窗口。
- return;: 结束函数执行。
move = move->next;: 将指针move移动到链表的下一个节点。
printf("未找到消费信息\n");: 如果循环结束还没有找到匹配的记录,打印提示信息表示没有找到消费信息。
system("pause");: 暂停程序执行。
system("cls");: 清空控制台窗口。
查询函数:
该模块用void modifyStudent(Node *head)和savestudent(head)两个函数实现,其中modifystudent(Node *head)函数的功能是输入要修改的消费时间,其参数为指向Node类型指针变量。在链表中查找与输入的消费信息相同的节点,如果找到就删除当前节点。使用While循环来历遍链表,如果未找到,则输出未找到消费信息。删除完毕后,调用saveStudent(head),是更改后的信息存储到文件中。
排序:
该模块使用void sortStudent(Node *head)和printStudent(head)两个函数实现的。在sotrStudent(Node *head)函数中定义两个指针a和b,用于遍历链表中的节点。使用两个嵌套的for循环遍历链表中的所有节点。外层循环变量a从链表的头节点开始,内层循环变量b从a的下一个节点开始。在内层循环中,比较节点a和节点b中的学生价格。如果节点a中的学生价格大于节点b中的学生价格,则交换这两个节点的学生信息。这是通过临时变量temp来实现的。在排序完成后,调用printStudent(head)函数来打印排序后的学生链表。这假定有一个名为printStudent的函数,它能够遍历并打印链表中的学生信息。
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<string.h>
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:4996)
#include<Windows.h>
void color(short x)
{
if (x >= 0 && x <= 15)
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);
else
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);
}
typedef struct Student
{
char date[10];
char name[10];
int price;
}Student;
typedef struct _Node
{
Student student;
struct _Node* next;
}Node;
void welcome();
void inputStudent(Node* head);//输入
void printStudent(Node* head);//输出
void countStudent(Node* head);//统计
void findStudent(Node* head);//查询
void saveStudent(Node* head);//存储
void loadStudent(Node* head);//读取
void modifyStudent(Node* head);//修改
void deletStudent(Node* head);//删除
void sortStudent(Node* head);//排序
//主函数
int main()
{
char c;
Node* head = (Node*)malloc(sizeof(Node));
head->next = NULL;
loadStudent(head);
while (1)
{
welcome();
c = _getch();
switch (c)
{
case '1': //录入消费信息
inputStudent(head);
break;
case '2': //打印消费信息
printStudent(head);
break;
case '3': //统计消费金额
countStudent(head);
break;
case '4': //查找消费信息
findStudent(head);
break;
case '5': //修改消费信息
modifyStudent(head);
break;
case '6': //删除消费信息
deletStudent(head);
break;
case'7'://将消费信息排序
sortStudent(head);
break;
case '8': //退出系统
system("cls");
color(7);
printf("感谢使用!\n");
break;
default:
color(7);
printf("请重新选择功能!\n");
break;
}
}
return 0;
}//主函数
//页面
void welcome()
{
color(11);
printf(" *********************************\n");
color(14);
printf(" *\t消费信息管理系统\t*\n");
color(11);
printf(" *********************************\n");
color(14);
printf(" *\t请选择功能列表\t *\n");
color(11);
printf(" *********************************\n");
color(14);
printf(" *\t 1.录入消费信息\t*\n");
color(14);
printf(" *\t 2.打印消费信息\t*\n");
color(14);
printf(" *\t 3.统计消费金额\t*\n");
color(14);
printf(" *\t 4.查找消费信息\t*\n");
color(14);
printf(" *\t 5.修改消费信息\t*\n");
color(14);
printf(" *\t 6.删除消费信息\t*\n");
color(14);
printf(" *\t 7.整理消费信息\t*\n");
color(14);
printf(" *\t 8.退出系统\t *\n");
color(11);
printf(" *********************************\n");
}
//录入消费信息
void inputStudent(Node* head)
{
Node* move = head;
Node* fresh = (Node*)malloc(sizeof(Node));
fresh->next = NULL;
color(7);
printf("请输入消费消费的时间,种类,金额\n");
color(11);
scanf("%s%s%d", fresh->student.date, fresh->student.name, &fresh->student.price);
while (move->next != NULL)
{
move = move->next;
}
move->next = fresh;
saveStudent(head);
system("pause");
system("cls");
}
//打印所储存的所有信息
void printStudent(Node* head)
{
Node* move = head->next;
while (move != NULL)
{
color(7);
printf("时间:%s 种类:%s 金额:%d\n", move->student.date, move->student.name, move->student.price);
move = move->next;
}
system("pause");
system("cls");
}
//统计所有消费次数
void countStudent(Node* head)
{
int count = 0;
Node* move = head->next;
while (move != NULL)
{
count += move->student.price;
move = move->next;
}
printf("消费的总金额为%d\n", count);
system("pause");
system("cls");
}
//查找消费记录
void findStudent(Node* head)
{
int a;
a = 0;
Node* move = head->next;
char date[20];
color(7);
printf("请输入要查找的消费的时间:");
color(11);
scanf("%s", date);
while (move != NULL)
{
if (strcmp(date, move->student.date) == 0)
{
color(7);
printf("时间:%s 种类:%s 金额:%d\n", move->student.date, move->student.name, move->student.price);
a++;
}
move = move->next;
if (move == NULL && a == 0)
{
printf("未找到消费信息\n");
break;
}
}color(7);
system("pause");
system("cls");
return;
}
//保存消费信息
void saveStudent(Node* head)
{
FILE* file = fopen("./Consumption.txt", "w");
Node* move = head->next;
while (move != NULL)
{
if (fwrite(&move->student, sizeof(Student), 1, file) != 1)
{
color(7);
printf("保存失败\n");
return;
}
move = move->next;
}
fclose(file);
}
//加载消费数据信息
void loadStudent(Node* head)
{
FILE* file = fopen("./Consumption.txt", "r");
Node* fresh = (Node*)malloc(sizeof(Node));
Node* move = head;
fresh->next = NULL;
if (!file)
{
color(7);
printf("没有查询到消费文件,跳过读取\n");
return;
}
while (fread(&fresh->student, sizeof(Student), 1, file) == 1)
{
move->next = fresh;
move = fresh;
fresh = (Node*)malloc(sizeof(Node));
fresh->next = NULL;
}
free(fresh);
fclose(file);
color(7);
printf("读取成功\n");
system("cls");
}
//修改消费数据信息
void modifyStudent(Node* head)
{
char date[20];
Node* move = head->next;
color(7);
printf("请输入要修改的消费的时间:");
color(11);
scanf("%s", date);
while (move != NULL)
{
if (strcmp(move->student.date, date) == 0)
{
color(7);
printf("请输入更改的消费种类,金额\n");
color(11);
scanf("%s%d", move->student.name, &(move->student.price));
color(7);
printf("修改成功\n");
saveStudent(head);
system("pause");
system("cls");
return;
}
move = move->next;
}
color(7);
printf("未找到消费信息。\n");
system("pause");
system("cls");
}
//删除信息
void deletStudent(Node* head)
{
char date[20];
Node* move = head;
color(7);
printf("请输入要删除的消费时间:");
color(11);
scanf("%s", date);
while (move != NULL)
{
if (strcmp(date, move->next->student.date) == 0)
{
Node* lin = move->next;
move->next = move->next->next;
free(lin);
saveStudent(head);
system("pause");
system("cls");
return;
}
move = move->next;
}color(7);
printf("未找到消费信息。\n");
system("pause");
system("cls");
}
//排序
void sortStudent(Node* head) {
Node* a, * b;
for (a = head; a->next != NULL; a = a->next) {
for (b = a->next; b != NULL; b = b->next) {
if (a->student.price > b->student.price) {
Student temp = a->student;
a->student = b->student;
b->student = temp;
}
}
}
printStudent(head);
}