先给出要求:
设计一个KTV点歌系统,该系统的主要功能是管理员对歌曲进行管理、统计等操作、以及用户根据关键字或风格等对歌曲进行查询等操作 |
|
我这个代码运用到的算法有二叉排序树(实现排行榜的功能),kmp算法(实现模糊查询)
没有任何套路,完全是自己手搓,大概600多行,如果觉得帮到你了,麻烦点个赞谢谢!!
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <windows.h>
#define MAX_SONGS 1000
#define MAX_ARTISTS 1000
// 歌曲结构体
typedef struct {
char name[100];
char artist[100];
char genre[50];
int rating_count; //点到该首歌的次数
float rating; //该首歌的评分
} Song;
// 用户结构体
typedef struct {
char username[50];
char password[50];
bool isAdmin; //判断是否为管理员,如果是就为1,若不是就为0
} User;
// 歌曲列表结构体
typedef struct {
Song songs[MAX_SONGS];//表示全部歌曲的集合
int count; //记录一共有多少首,起到一个循环调用控制作用
} SongList;
// 用户列表结构体
typedef struct {
User users[MAX_ARTISTS];
int count;
} UserList;
// 树节点结构体
typedef struct tree_node {
Song* song;
struct tree_node* left;
struct tree_node* right;
} TreeNode;
void insert_song(TreeNode** root, Song* song);
void inorder_traversal(TreeNode* root);
void generate_ranking(SongList* list);
Song* findSong(SongList* list, const char* name,int flag);
User* adminLoginView(UserList* users, SongList* list);
User* userLoginView(UserList* users, SongList* list);
void adminMenu();
void userMenu();
void addSongView(SongList* list);
void removeSongView(SongList* list);
void updateSongView(SongList* list);
void countHotnessView(SongList* list);
void searchSongView(SongList* list);
void rateSongView(SongList* list, Song* song);
void daily_recommendation(SongList list);
//管理员注册
void register_ADM(UserList* userlist) {
system("cls");
printf("\n**************************欢迎来到ktv点歌系统***********************************\n");
printf("注册请填写以下信息:\n");
printf("请输入您的账户名字(不超过 32 个字符):");
scanf("%s", userlist->users[userlist->count].username);
getchar();
printf("密码(不超过 32 个字符):");
scanf("%s", userlist->users[userlist->count].password);
// 保存用户信息到文件中
FILE* fp;
int user_count = userlist->count;
if ((fp = fopen("users.txt", "a")) != NULL) {
fprintf(fp, "%s %s %d\n", userlist->users[user_count].username, userlist->users[user_count].password, 1);
fclose(fp);
printf("注册成功,请返回上一级,重新登录!\n");
}
else {
printf("无法打开文件!\n");
}
getchar();
getchar();
return;
}
//用户注册
void register_user(UserList * userlist) {
system("cls");
printf("\n**************************欢迎来到ktv点歌系统***********************************\n");
printf("注册请填写以下信息:\n");
printf("请输入您的账户名字(不超过 32 个字符):");
scanf("%s", userlist->users[userlist->count].username);
getchar();
printf("密码(不超过 32 个字符):");
scanf("%s", userlist->users[userlist->count].password);
// 保存用户信息到文件中
FILE* fp;
int user_count = userlist->count;
if ((fp = fopen("users.txt", "a")) != NULL) {
fprintf(fp, "%s %s %d\n", userlist->users[user_count].username, userlist->users[user_count].password, 0);
fclose(fp);
printf("注册成功,请返回上一级,重新登录!\n");
}
else {
printf("无法打开文件!\n");
}
getchar();
getchar();
return;
}
//构建二叉排序树
void insert_song(TreeNode** root, Song* song) {
// 如果树为空,直接将歌曲作为根节点
if (*root == NULL) {
*root = (TreeNode *)malloc(sizeof(TreeNode));
(*root)->song = song;
(*root)->left = NULL;
(*root)->right = NULL;
return;
}
// 如果树非空,则根据歌曲的热度值进行插入
if (song->rating_count >= (*root)->song->rating_count) {
insert_song(&((*root)->left), song);
}
else {
insert_song(&((*root)->right), song);
}
}
// 显示歌曲列表
void show_song_list(SongList* songlist) {
int num=1;
printf("===============================================================\n");
printf("歌曲编号\t歌曲名称\t歌手\t\t类型\t评分\n");
printf("===============================================================\n");
for (int i = 0; i < songlist->count; i++) {
printf("%-8d\t%-10s %-10s\t%s\t%.2f\n",num++, songlist->songs[i].name, songlist->songs[i].artist, songlist->songs[i].genre, songlist->songs[i].rating);
}
printf("===============================================================\n");
}
//点歌
void play_song(SongList* songlist) {
system("cls");
show_song_list(songlist);
int song_id=0;
printf("请输入你要点歌的编号:");
scanf("%d", &song_id);
printf("正在播放《%s》演唱者:%s\n", songlist->songs[song_id-1].name, songlist->songs[song_id-1].artist);
getchar();
getchar();
rateSongView(songlist,&songlist->songs[song_id-1]);
}
// 中序遍历树并输出歌曲信息
void inorder_traversal(TreeNode* root) {
static int count = 0;
if (root != NULL) {
inorder_traversal(root->left);
printf("第%d. %s - %s (%s, hotness: %.2d 评分: %.2f)\n", ++count, root->song->artist, root->song->name, root->song->genre, root->song->rating_count, root->song->rating);
inorder_traversal(root->right);
}
}
// 对歌曲进行分类并生成排行榜
void generate_ranking(SongList* list) {
TreeNode* root = NULL;
// 将歌曲插入二叉排序树中并按热度值排序
for (int i = 0; i < list->count; i++) {
insert_song(&root, &(list->songs[i]));
}
inorder_traversal(root);
getchar();
getchar();
}
//输出全部歌曲
void getlist(SongList* list)
{
for (int i = 0; i < list->count; i++)
{
printf("%s - %s (%s, hotness: %d, rating: %.2f)\n", list->songs[i].artist, list->songs[i].name, list->songs[i].genre, list->songs[i].rating_count, list->songs[i].rating);
}
getchar();
getchar();
}
//获取next数组
void GetNext(char* pattern, int len, int* next) {
int i = 0, j = next[0] = -1;
while (i < len - 1) {
if (j == -1 || pattern[i] == pattern[j]) {
++i;
++j;
next[i] = (pattern[i] != pattern[j]) ? j : next[j];
}
else {
j = next[j];
}
}
}
//基于kmp算法的模糊查询
int kmp(char* text,int n,char* pattern,int m) {
int* next = (int*)malloc(sizeof(int) * m);
GetNext(pattern, m, next);
int count = 0;
// text和pattern的下标
int i = 0, j = 0;
while (i < n&&j<m) {
if (j == -1 || text[i] == pattern[j]) {
++i;
++j;
}
else {
j = next[j];
}
}
if (j >= m)
return 1;
else return 0;
}
//模糊查找函数
Song* findSong(SongList* list,char* keyword,int flag) {
int found=0;
printf("已找到下列歌曲:\n");
printf("===============================================================\n");
printf("歌曲名称\t歌手\t\t类型\t评分\n");
printf("===============================================================\n");
for (int i = 0; i < list->count; i++) {
if (kmp(list->songs[i].name, strlen(list->songs[i].name), keyword, strlen(keyword))||kmp(list->songs[i].artist, strlen(list->songs[i].artist), keyword, strlen(keyword)) ||
kmp(list->songs[i].genre, strlen(list->songs[i].genre), keyword, strlen(keyword))) {
printf("%-10s %-10s\t%s\t%.2f\n", list->songs[i].name, list->songs[i].artist, list->songs[i].genre, list->songs[i].rating);
found = 1;
}
}
if (!found) printf("未找到匹配的歌曲!\n");
if(flag==0)
{
printf("是否继续查找歌曲?\n");
int choose;
printf("1.继续查找\n");
printf("2.返回上一级\n");
scanf("%d", &choose);
if (choose == 1)
{
system("cls");
searchSongView(list);
}
else return;
}
}
// 管理员登录界面
User* adminLoginView(UserList* users, SongList* list) {
char username[50];
char password[50];
int flag = 0;
while (flag == 0)
{
printf("管理员账号\n ");
scanf("%s", username);
getchar();
printf("密码:\n");
scanf("%s", password);
int i;
for (i = 0; i < users->count; i++) {
if (strcmp(users->users[i].username, username) == 0 && strcmp(users->users[i].password, password) == 0 && users->users[i].isAdmin) {
flag = 1;
break;
}
}
if (i >= users->count) {
printf("账户或者密码输入错误,请重新输入\n"); flag = 0;
}
}
while(1)
{
int choice;
system("cls");
printf("欢迎回来,尊敬的%s\n", username);
printf("请选择你想要执行的操作:\n ");
adminMenu();
scanf("%d", &choice);
switch (choice) {
case 1:
system("cls");
addSongView(list);
break;
case 2:
system("cls");
removeSongView(list);
break;
case 3:
system("cls");
getlist(list);
break;
case 4:
system("cls");
countHotnessView(list);
break;
case 5:
return;
break;
default:
printf("错误的选择!!\n");
}
}
return NULL;
}
// 用户登录界面
User* userLoginView(UserList* users, SongList* list) {
char username[50];
char password[50];
int flag = 0;
while(flag==0)
{
printf("用户名:");
scanf("%s", username);
getchar();
printf("密码:");
scanf("%s", password);
int i;
for (i = 0; i < users->count; i++) {
if (strcmp(users->users[i].username, username) == 0 && strcmp(users->users[i].password, password) == 0 && !users->users[i].isAdmin) {
flag = 1;
break;
}
}
if (i >= list->count) {
printf("请重新输入账户和密码!\n"); flag = 0;
system("cls");
}
}
system("cls");
printf("用户登陆成功!\n");
while (1)
{
daily_recommendation(*list);
// 用户登录状态下显示用户菜单
userMenu();
int choice;
printf("请输入你想要实现的功能:\n ");
scanf("%d", &choice);
switch (choice) {
case 1:
system("cls");
searchSongView(list);
break;
case 2:
system("cls");
play_song(list);
break;
case 3:
system("cls");
return 0;
break;
default:
printf("Error: Invalid choice.\n");
}
}
return NULL;
}
// 管理员菜单
void adminMenu() {
printf("\n请问您要执行哪项功能?\n");
printf("1. 添加歌曲\n");
printf("2. 删除歌曲\n");
printf("3. 查找歌曲\n");
printf("4. 查看热度值排名\n");
printf("5. 登出!\n");
}
// 用户菜单
void userMenu() {
printf("\n请问您还要执行哪些功能呢?\n");
printf("1. 查找歌曲\n");
printf("2. 点歌\n");
printf("3. 登出\n");
}
// 添加歌曲界面
void addSongView(SongList* list) {
printf("\n**************************添加歌曲***********************************\n");
char name[100];
char artist[100];
char genre[50];
float hotness;
getchar();
printf("歌名: ");
gets(name);
printf("歌手: ");
gets(artist);
printf("风格: ");
gets(genre);
if (list->count >= MAX_SONGS) {
printf("歌库满了,请删点歌吧\n");
return;
}
//在尾部插入歌曲信息
strcpy(list->songs[list->count].name, name);
strcpy(list->songs[list->count].artist, artist);
strcpy(list->songs[list->count].genre, genre);
list->songs[list->count].rating_count = 0;
list->songs[list->count].rating = 0.0;
//一开始都初始化为0,因为没有人点
list->count++;
FILE* fp = fopen("songs.txt", "a");
if (fp != NULL) {
fprintf(fp, "%s %s %s %d %.2f\n", name, artist, genre,0, 0.0);
fclose(fp);
printf("歌曲添加成功!\n");
printf("是否继续添加歌曲?");
int choose;
printf("1.继续添加");
printf("2.返回上一级");
scanf("%d", &choose);
if (choose == 1)
{
system("cls");
addSongView(list);
}
else return;
}
else {
printf("打不开文件\n");
}
}
// 删除歌曲界面
void removeSongView(SongList* list) {
printf("\n**************************删除歌曲***********************************\n");
show_song_list(list);
char name[100];
getchar();
printf("请选择需要删除的歌名: ");
gets(name);
int i; //bug!!!!!根本就不执行下面的for直接就退出了
for (i = 0; i < list->count; i++) {
if (strcmp(list->songs[i].name, name) == 0) {
printf("成功移除歌曲: %s\n", name);
getchar();
list->count--;
for (int j=i; j< list->count; j++) {
list->songs[j] = list->songs[j + 1];
}
//将歌曲从list数组中删除
FILE* fp = fopen("songs.txt", "w");
if (fp != NULL) {
for (int i = 0; i < list->count; i++) {
fprintf(fp, "%s %s %s %d %.2f\n",
list->songs[i].name, list->songs[i].artist, list->songs[i].genre,
list->songs[i].rating_count, list->songs[i].rating);
}
fclose(fp);
}
else {
printf("打开文件错误!!\n");
}
break;
}
}
printf("是否继续删除歌曲?\n");
int choose;
printf("1.继续删除\n");
printf("2.返回上一级\n");
scanf("%d", &choose);
if (choose == 1)
{
system("cls");
removeSongView(list);
}
else return;
}
// 统计歌曲火热度界面
void countHotnessView(SongList* list) {
printf("************************歌曲火热度排行榜*******************************\n");
generate_ranking(list);
}
// 搜索歌曲界面
void searchSongView(SongList* list) {
printf("\n************************搜索歌曲*******************************\n");
char name[100];
int count=0;
printf("请输入歌名或歌名或风格:");
scanf("%s", name);
findSong(list, name,0);
system("cls");
}
//每日随机推荐
void daily_recommendation(SongList list) {
// 使用当前时间戳作为随机数种子
srand((unsigned)time(NULL));
// 随机选择一首歌曲
int song_index = rand() % list.count;
Song* recommended_song = &(list.songs[song_index]);
printf("今日推荐:[%s] - %s (%s),评分:%.2f\n",
recommended_song->name,
recommended_song->artist,
recommended_song->genre,
recommended_song->rating);
}
// 对歌曲进行评分界面
void rateSongView(SongList* list,Song* song) {
system("cls");
printf("\n**************************歌曲评分***********************************\n");
char name[100];
if (song != NULL) {
int rating;
printf("1-5分你打几分?: ");
scanf("%d", &rating);
if (rating >= 1 && rating <= 5) {
song->rating_count++;
song->rating = (song->rating * (song->rating_count - 1) + rating) / song->rating_count;//统计歌曲的评分
FILE* fp = fopen("songs.txt", "w");
if (fp != NULL) {
for (int i = 0; i < list->count; i++) {
fprintf(fp, "%s %s %s %d %.2f\n",
list->songs[i].name, list->songs[i].artist, list->songs[i].genre,
list->songs[i].rating_count, list->songs[i].rating);
}
fclose(fp);
printf("评分成功%s\n", song->name);
}
else {
printf("没有找到文件\n");
}
}
else {
printf("错误的输入\n");
}
}
else {
printf("没有找到该歌曲.\n");
}
getchar();
getchar();
system("cls");
}
void init_song_list(SongList* songlist) {
songlist->count = 0;
}
int main() {
FILE* fp = fopen("songs.txt", "r");
SongList list;
init_song_list(&list);
if (fp != NULL) {
while (list.count < MAX_SONGS) {
Song new_song;
int result = fscanf(fp, "%s %s %s %d %f\n", new_song.name, new_song.artist, new_song.genre, &(new_song.rating_count), &(new_song.rating));
if (result == 5) {
// 成功读取一行,将新歌曲加入列表
list.songs[list.count++] = new_song;
}
else if (result == EOF) {
// 已到达文件末尾,退出循环
break;
}
else {
// 格式错误,忽略该行,并继续读取下一行
char buffer[256];
fgets(buffer, 256, fp);
}
}
fclose(fp);
}
else {
printf("打开文件失败!\n");
return 1;
}
// printf("%d\n", list.count); // 从文件中读取用户列表
fp = fopen("users.txt", "r");
UserList userList;
userList.count = 0;
if (fp != NULL) {
while (fscanf(fp, "%s %s %d\n", userList.users[userList.count].username, userList.users[userList.count].password, &(userList.users[userList.count].isAdmin)) == 3) {
userList.count++;
}
fclose(fp);
}
else {
printf("打开文件失败!\n");
}
User* currentUser = NULL;
while (1) {
if (currentUser == NULL) {
// 未登录状态下显示登录界面
printf("\n\t\t***************************************************************\n");
printf("\t\t*欢迎光临咱家的ktv,祝你在这里能玩的开心!请选择下列两种登录方式 *\n");
printf("\n\t\t**********************************************\n");
printf("\t\t* 1. 管理员登录 *\n");
printf("\t\t* 2. 用户登录 *\n");
printf("\t\t* 3. 用户注册 *\n");
printf("\t\t* 4. 管理员注册 *\n");
printf("\t\t* 5. 退出 *\n");
printf("\t\t**********************************************\n\n");
int choice;
printf("\t\t请选择需要实现的功能: ");
scanf("%d", &choice);
switch (choice) {
case 1:
adminLoginView(&userList,&list);
system("cls");
break;
case 2:
userLoginView(&userList,&list);
system("cls");
break;
case 3:
register_user(&userList);
system("cls");
break;
case 4:
register_ADM(&userList);
system("cls");
break;
case 5:
exit(0);
default:
printf("Error: Invalid choice.\n");
}
}
}
return 0;
}