目录
前言
基于c语言基础链表、文件、动态内存知识设计的游戏库管理系统,包含登录注册、增删查改排序等基础功能。
编译软件:Visual Studio 2022
一.系统功能
Ⅰ.登录界面:
1.登录功能;
2.注册功能;
Ⅱ.功能界面
1.查看列表信息;
2.删除入库游戏;
3.新增入库游戏;
4.修改入库游戏;
5.查找相关标签;
6.排序游戏信息;
7.猜你喜欢;
二.基本框架
将系统运行过程中需要运用到的基础库头文件,结构体数组,链表定义,函数声明封装到自定义的头文件GameManage.h中。
Ⅰ.结构体部分
用于储存录入的账号以及游戏数据。
登陆界面部分结构体包含以下基本信息:账号,密码,再次输入密码,分别用account,keyword,rekeword表示,代码如下:
typedef struct{
int account;
int keyword;
int rekeyword;
}user;//登录注册
功能界面部分结构体包含以下信息: 编号,名称,类型,价格,喜爱程度,推荐程度,分别用id,name,type,price,likelevel,recommend表示,代码如下:
typedef struct {
int id;
char name[66];
char type[66];
int price;
int likelevel;
int recommend;
}game;
定义链表中节点,头节点,代码如下:
typedef struct{
user users;
game games;
struct Node* next;
}Node;//节点
typedef struct
{
Node* First;
int size;
}List;//头节点
Ⅱ.函数部分
登陆界面使用到的函数,代码如下:
void readdate();//读取信息
int menumap();//登录界面
int regist();//注册(存入账号信息
int login();//登录(读取账号信息
功能界面使用到的函数,代码如下:
int mainmanu();//功能界面菜单
void readdate2();//读取信息
int writein();//写入文件
int print();//查看(打印
int add();//新增
int change();//修改
int del();//删除
int find();//找标签
int mainsort();//排序
int likesort();
int pricesort();
int guess();//猜测喜好
void elsereaddate();//读取推荐文件
int add3();//向推荐txt文件内增添信息
int writein3();//写入推荐txt文件中
Ⅲ.主函数
选择,循环功能
登陆界面:通过switch case语句实现通过用户输入值选择功能。使用while(1)无限循环,使得用户输入退出选择值才实现退出。代码如下:
while (1)
{
switch (menumap())//登陆界面
{
case(1)://登录
{
if (login(&list1))
{...}//具体代码放在功能界面
else
break;
};
case(2)://注册
{
system("cls");
regist(&list1);
break;
}
case(0):
{
system("cls");
printf("\n\n\n\n\n");
printf(" ****感谢您的使用!****\n\n\n\n\n\n");
system("pause");
return 0;
}
}
system("pause");
system("cls");
}
功能界面:成功登录后调用readdate函数读取Game.txt文件中已存在数据,每次选择操作结束后将新数据写入Game.txt文件中。代码如下:
if (login(&list1))
{
readdate2(&list2);
while (1)
{
writein(&list2);
switch (mainmanu())
{
case(1)://查
{
system("cls");
print(&list2);
break;
}
case(2)://删
{
system("cls");
del(&list2);
flat = 0;
break;
}
case(3)://增
{
system("cls");
add(&list2);
flat = 0;
break;
}
case(4)://改
{
system("cls");
change(&list2);
flat = 0;
break;
}
case(5)://找
{
system("cls");
find(&list2);
break;
}
case(6)://排
{
system("cls");
mainsort(&list2);
break;
}
case(7)://猜
{
int temp = 0;
system("cls");
guess(&list2,&list3,&flat,temp);
break;
}
case(8)://增添推荐文件数据
{
int t = 0;
printf("输入t=");
scanf("%d", &t);
system("cls");
add3(&list3);
writein3(&list3,t);
flat = 0;
break;
}
case(0):
{
system("cls");
printf("\n\n\n\n\n");
printf(" ****感谢您的使用!****\n\n\n\n\n\n");
system("pause");
return 0;
}
}
system("pause");
system("cls");
}
}
else
break;
}
三.功能实现
Ⅰ.登录界面
1.登陆界面菜单
打印界面菜单,显示必要的提示语句
代码如下:
int menumap()
{
printf("\n\n\n\n\n\n\n");
printf(" *****************************************************\n");
printf(" ** 欢迎使用游戏库管理系统 **\n");
printf(" *****************************************************\n");
printf(" ** 请登录或创建一个新的账号 **\n");
printf(" *****************************************************\n");
printf(" ** 1.登录 **\n");
printf(" ** 2.注册 **\n");
printf(" ** 0.退出 **\n");
printf(" *****************************************************\n");
printf(" select>");
int select = -1;
scanf("%d", &select);
return select;
}
2.注册功能
定义新节点,录入账号信息,通过while,if操作实现再次确认密码,通过头插操作连入链表。遍历节点写入Account.txt文件。代码如下:
int regist(List*list1)
{
Node* node = malloc(sizeof(Node));
if (!node)
{
printf("malloc node failed");
return 0;
}
node->next = NULL;
printf("请输入账号>");
scanf("%d", &node->users.account);
printf("请输入密码>");
scanf("%d", &node->users.keyword);
while (1)
{
printf("请再次确认密码>");
scanf("%d", &node->users.rekeyword);
if (node->users.rekeyword == node->users.keyword)
{
printf("注册成功!\n");
break;
}
else
{
printf("与原密码不一致!请重新输入!\n");
continue;
}
}
node->next = list1->First;
list1->First = node;
list1->size++;
FILE* fp = fopen("Account.txt", "w");
if (!fopen)
{
printf("fopen failed");
return 0;
}
Node* curNode = list1->First;
while (curNode != NULL)
{
fprintf(fp, "Acount:%d\tKeyword:%d\t\n", curNode->users.account, curNode->users.keyword);
curNode = curNode->next;
}
fclose(fp);
return 0;
}
3.登录功能
定义now新结构体变量,输入账号密码,遍历链表,若账号不存在则提示用户并返回菜单,若检测账号存在则进一步检测密码是否正确,不正确则重复输入操作,代码如下:
int login(List*list1)
{
printf("\n\n\n\n\n\n");
printf("** 请输入账号>");
user now;
scanf("%d",&now.account);
printf("** 请输入密码>");
scanf("%d", &now.keyword);
Node *curnode = list1->First;
while (curnode != NULL)
{
if (now.account == curnode->users.account)
{
while (now.keyword != curnode->users.keyword)
{
printf("密码错误!请重新输入密码!\n");
printf("请输入密码>");
scanf("%d", &now.keyword);
}
printf("登陆成功!\n");
system("pause");
system("cls");
return 1;
}
else
{
curnode = curnode->next;
continue;
}
}
printf("没有找到您的账户信息!请注册!\n");
return 0;
}
Ⅱ.功能界面
1.功能界面菜单
代码如下:
int mainmanu()
{
printf("\n\n\n\n\n");
printf(" *****************************************************\n");
printf(" * 欢迎使用游戏库管理系统 *\n");
printf(" *****************************************************\n");
printf(" * 请选择功能 *\n");
printf(" *****************************************************\n");
printf(" * 1.查看列表信息 2.删除入库游戏 *\n");
printf(" * 3.新增入库游戏 4.修改游戏信息 *\n");
printf(" * 5.查找相关标签 6.排序游戏信息 *\n");
printf(" * 7.猜你喜欢o.o *\n");
printf(" *****************************************************\n");
printf(" * 0.退出 *\n");
printf(" *****************************************************\n");
printf(" select>");
int select = -1;
scanf("%d", &select);
return select;
}
3.查询已有游戏数据
遍历链表并打印,代码如下:
int print(List*list2)
{
Node* curnode = list2->First;
printf("***编号****名称*********标签****标价***喜爱程度***\n");
while (curnode != NULL)
{
printf("%d%15s%9s%7d%7d\n",curnode->games.id,curnode->games.name, curnode->games.type, curnode->games.price, curnode->games.likelevel);
curnode = curnode->next;
}
return 0;
}
3.新增游戏数据
malloc开空间时注意大小写(把Node写成node被越界卡哭了qwq)代码如下:
int add(List*list2)
{
Node* node = malloc(sizeof(Node));
if (!node)
{
printf("malloc node failed");
return 0;
}
node->next = NULL;
printf("请输入游戏编号>");
scanf("%d", &node->games.id);
printf("请输入游戏名称>");
scanf("%s", node->games.name);
printf("请输入游戏主要标签\n(动作、冒险、模拟、角色扮演、休闲和其他)>");
scanf("%s",node->games.type);
printf("请输入游戏标价>");
scanf("%d", &node->games.price);
printf("请输入对该游戏喜爱程度>");
scanf("%d", &node->games.likelevel);
node->next = list2->First;
list2->First = node;
list2->size++;
return 0;
}
4.删除入库游戏数据
定义新节点pevnode记录curnode前一个节点,以实现删去对应节点后链表仍然完整。通过判断pevnode是否为空解决欲删节点为第一个节点的问题。
若遍历后未找到输入游戏编号,则提示用户退出或重新输入。
代码如下:
int del(List*list2)
{
print(list2);
printf("\n\n\n");
printf("请输入欲删除的游戏编号>");
int delid;
scanf("%d", &delid);
Node* curnode = list2->First;
Node* pevnode = NULL;
while (curnode != NULL)
{
if (curnode->games.id != delid)
{
pevnode = curnode;
curnode = curnode->next;
}
else
{
if (pevnode == NULL)
{
list2->First = curnode->next;
free(curnode);
}
else
{
pevnode->next = curnode->next;
free(curnode);
}
printf("成功删除!");
return 0;
}
}
if (curnode == NULL)
{
int date = -1;
printf("未找到该游戏!\n");
printf("1.重新输入游戏名\n");
printf("2.退回功能页面\n");
printf(">");
scanf("%d", &date);
switch (date)
{
case(1):
{
system("cls");
change(list2);
}
case(2):
{
return 0;
}
}
}
return 0;
}
5.修改已入库游戏数据
遍历链表,找到则提示用户选择修改数据,通过switch case语句实现。代码如下:
int change(List*list2)
{
print(list2);
printf("\n\n\n");
int willchangeid;
printf("请输入欲修改的游戏编号>");
scanf("%d", &willchangeid);
Node* curnode = list2->First;
while (curnode != NULL)
{
if(curnode->games.id==willchangeid)
{
int p = 0;
printf("请选择要修改的数据>\n");
printf("1.编号\n2.名称\n3.类型\n4.标价\n5.喜爱程度\n");
scanf("%d", &p);
switch (p)
{
case(1):
{
printf("请输入修改后的游戏编号>");
scanf("%d", &curnode->games.id);
break;
}
case(2):
{
printf("请输入修改后的游戏名>");
scanf("%s", curnode->games.name);
break;
}
case(3):
{
printf("请输入修改后的游戏类型\n(动作、冒险、模拟、角色扮演、休闲和其他)>");
scanf("%s", curnode->games.type);
break;
}
case(4):
{
printf("请输入修改后的游戏标价>");
scanf("%d", &curnode->games.price);
break;
}
case(5):
{
printf("请输入修改后的喜爱程度>");
scanf("%d", &curnode->games.likelevel);
break;
}
}
system("pause");
system("cls");
printf("修改成功!\n");
return 0;
}
curnode = curnode->next;
}
if (curnode == NULL)
{
int date = -1;
printf("未找到该游戏!\n");
printf("1.重新输入游戏名\n");
printf("2.退回功能页面\n");
printf(">");
scanf("%d", &date);
switch (date)
{
case(1):
{
system("cls");
change(list2);
}
case(2):
{
return 0;
}
}
}
return 0;
}
6.寻找相关标签
遍历链表,定义新变量计数,代码如下:
int find(List*list2)
{
char type[44];
int t = 0;
printf("请输入标签(动作、冒险、模拟、角色扮演、休闲和其他)\n>");
scanf("%s", type);
Node* curnode = list2->First;
while (curnode != NULL)
{
if (strcmp(curnode->games.type, type)==0)
{
if (t == 0)
{
t = 1;
printf("***编号****名称*********标签****标价***喜爱程度***\n");
printf("%d\t%s\t%s\t%d\t%d\n", curnode->games.id, curnode->games.name, curnode->games.type, curnode->games.price, curnode->games.likelevel);
}
else
printf("%d\t%s\t%s\t%d\t%d\n", curnode->games.id, curnode->games.name, curnode->games.type, curnode->games.price, curnode->games.likelevel);
}
curnode = curnode->next;
}
if (t == 0)
{
printf("未找到含有该标签游戏!\n");
}
return 0;
}
7.排序已入库游戏
提示用户选择排序方式,使用switch case语句调用具体排序函数,代码如下:
int mainsort(List*list2)
{
int select = -1;
printf("请选择排序方式:\n");
printf("1.喜爱程度(降序)\n");
printf("2.标价(升序)\n");
printf(">");
scanf("%d", &select);
system("cls");
printf("***编号****名称*********标签****标价***喜爱程度***\n");
switch (select)
{
case(1):
{
likesort(list2);
break;
}
case(2):
{
pricesort(list2);
break;
}
}
return 0;
}
具体排序函数内定义新结构体数组变量,遍历读取链表中结构体,进行排序(这里用的是最简单的冒泡排序,感觉用qsort函数直接排比较快),打印即可。代码如下:
int likesort(List*list2)
{
game ori[101];
int i = 0;
Node* curnode = list2->First;
while (curnode != NULL)
{
ori[i] = curnode->games;
curnode = curnode->next;
i++;
}
game temp;
for (int k = 0; k < i; k++)
{
for (int j = k; j < i; j++)
{
if (ori[k].likelevel < ori[j].likelevel)
{
temp = ori[k];
ori[k] = ori[j];
ori[j] = temp;
}
}
}
for (int k = 0; k < i; k++)
{
printf("%d%15s%9s%7d%7d\n", ori[k].id,ori[k].name,ori[k].type,ori[k].price,ori[k].likelevel);
}
return 0;
}
int pricesort(List*list2)
{
game ori[101];
int i = 0;
Node* curnode = list2->First;
while (curnode != NULL)
{
ori[i] = curnode->games;
curnode = curnode->next;
i++;
}
game temp;
for (int k = 0; k < i; k++)
{
for (int j = k; j < i; j++)
{
if (ori[k].price > ori[j].price)
{
temp = ori[k];
ori[k] = ori[j];
ori[j] = temp;
}
}
}
for (int k = 0; k < i; k++)
{
printf("%d%15s%9s%7d%7d\n", ori[k].id, ori[k].name, ori[k].type, ori[k].price, ori[k].likelevel);
}
return 0;
}
8.猜你喜欢o.o
设置整型数组初始化为0,自0~5分别表示动作、冒险、模拟、角色扮演、休闲、其他,遍历链表计算出现最多的游戏类型,调用elsereaddate向list3链表中读取相应推荐类型文件,遍历打印,代码如下:
int guess(List*list2,List*list3,int *flat,int temp)
{
int arr[7] = { 0 };//动作0、冒险1、模拟2、角色扮演3、休闲4和其他5
Node* curnode = list2->First;
while (curnode != NULL)
{
if (strcmp(curnode->games.type, "动作") == 0)
{
arr[0]++;
}
else if (strcmp(curnode->games.type, "冒险") == 0)
{
arr[1]++;
}
else if (strcmp(curnode->games.type, "模拟") == 0)
{
arr[2]++;
}
else if (strcmp(curnode->games.type, "角色扮演") == 0)
{
arr[3]++;
}
else if (strcmp(curnode->games.type, "休闲") == 0)
{
arr[4]++;
}
else if (strcmp(curnode->games.type, "其他") == 0)
{
arr[5]++;
}
curnode = curnode->next;
}
int max = -1,t=0;
for (int i = 0; i < 6; i++)
{
if (arr[i] > max)
{
max = arr[i];
t = i;
}
}
if (*flat == 0)
{
elsereaddate(list3, t);
*flat = 1;
}
Node* curnode1 = list3->First;
printf("***编号****名称*********推荐程度***\n");
while (curnode1 != NULL)
{
printf("%d%15s%8d\n",curnode1->games.id, curnode1->games.name, curnode1->games.recommend);
curnode1 = curnode1->next;
}
printf("\n\n\n已根据您已入库游戏进行推荐ovo\n");
return 0;
}
为解决不同类型游戏文件不同,定义FILE新函数接收guess函数传递的整数,向elsereaddate读取函数传递打开文件。代码如下:
FILE* actfile(int t)
{
FILE* fp = NULL;
switch (t) {
case 0:
fp = fopen("推荐游戏(动作.txt", "r");
break;
case 1:
fp = fopen("推荐游戏(冒险.txt", "r");
break;
case 2:
fp = fopen("推荐游戏(模拟.txt", "r");
break;
case 3:
fp = fopen("推荐游戏(角色扮演.txt", "r");
break;
case 4:
fp = fopen("推荐游戏(休闲.txt", "r");
break;
case 5:
fp = fopen("推荐游戏(其他.txt", "r");
break;
default:
printf("输入的参数不合法\n");
return NULL;
}
if (fp == NULL) {
perror("打开文件失败");
return NULL;
}
return fp;
}
void elsereaddate(List* list3,int t)
{
memset(list3, 0, sizeof(List));
FILE* fp = actfile(t);
if (!fp)
{
printf("fopen failed");
return 0;
}
while (!feof(fp))
{
Node* node = malloc(sizeof(Node));
node->next = NULL;
if (!node)
{
break;
}
if (3 != fscanf(fp, "id:%d\t\tname:%s\t\trecommend:%d\n", &node->games.id, &node->games.name, &node->games.recommend))
{
free(node);
break;
}
node->next = list3->First;
list3->First = node;
list3->size++;
}
fclose(fp);
return 0;
}
9.添加推荐游戏(不在提示菜单界面显示)
功能界面输入8后,提示用户输入t(0~5分别表示动作,冒险,模拟,角色扮演,休闲,其他),
定义新节点录入信息,如上方法解决写入文件函数问题,代码如下:
FILE* actfile2(int t)
{
FILE* fp = NULL;
switch (t) {
case 0:
fp = fopen("推荐游戏(动作.txt", "w");
break;
case 1:
fp = fopen("推荐游戏(冒险.txt", "w");
break;
case 2:
fp = fopen("推荐游戏(模拟.txt", "w");
break;
case 3:
fp = fopen("推荐游戏(角色扮演.txt", "w");
break;
case 4:
fp = fopen("推荐游戏(休闲.txt", "w");
break;
case 5:
fp = fopen("推荐游戏(其他.txt", "w");
break;
default:
printf("输入的参数不合法\n");
return NULL;
}
if (fp == NULL) {
perror("打开文件失败");
return NULL;
}
return fp;
}
int writein3(List* list3,int t)
{
Node* curnode = list3->First;
FILE* fp = actfile2(t);
if (!fp)
{
printf("fopen failed");
return 0;
}
while (curnode != NULL)
{
fprintf(fp,"id:%d\t\tname:%s\t\trecommend:%d\n", curnode->games.id, curnode->games.name, curnode->games.recommend);
curnode = curnode->next;
}
fclose(fp);
return 0;
}
总结
还存在问题的部分:猜猜部分读取文件和保存文件偶尔乱码偶尔正常,后续进行完善。(已解决)猜猜部分若入库游戏有数目相等的标签只会显示代表数字更小的标签。注册功能有待完善...
可以补充的部分:登录界面加上忘记密码,错误输入时加入次数限制。数据进一步扩展,尝试单个游戏添加多个标签,并在寻找标签功能中正确表示。
第一次写能实现一定功能的c语言项目,比较仓促和简陋,回顾一下感觉还有挺多可以填充的内容,后续再进行完善尝试。
写游戏相关也是比较新奇的体验,跟喜欢的东西联系起来就很有成就感!希望很久以后看到我还那么喜欢这些ovo
警示后人(打印出现乱码问题)
读取文件出现乱码的原因:编码问题。解决方法如下:
1.打开txt文件,点击右上角文件选项,点击另存为
2.检查修改右下角编码是否正确
改为ANSI即可正常显示