C语言实现歌手比赛评分系统:结构体与文件操作实战
本文展示了一个基于C语言的歌手比赛评分系统,通过结构体存储选手数据,实现从录入、10位评委打分、剔除极值求平均分、排名统计到文件保存的全流程功能。
一、 系统设计目标
本实例旨在解决简单的赛事成绩管理问题。核心需求包括:
- 数据管理:记录选手编号、姓名等基本信息。
- 评分算法:收集10位评委的打分,采用行业通用的“去掉一个最高分,去掉一个最低分”规则计算总分和平均分。
- 数据处理:实现成绩的动态排序(从高到低)和按编号查询。
- 持久化:将处理后的数据写入本地文件以备查看。
二、 界面与交互预览
系统运行后,控制台将显示主菜单供用户选择操作模式,各步骤的输入与输出界面如下所示。
系统启动后展示的基础菜单界面。
选手基本信息录入时的界面提示。
逐个输入评委打分的交互过程。
最终生成的成绩排名列表输出。
三、 核心逻辑解析
第1步:定义数据结构
为了高效管理多名选手的信息,使用 struct 定义结构体。每个结构体实例包含以下字段:
num: 选手编号。name: 选手姓名(字符数组)。score[10]: 存放10位评委的原始打分。aver: 计算后的平均分。sum: 原始总分(用于调试或备份)。ord: 根据平均分计算出的排名。
第2步:数据录入模块
在 singer_information 函数中,程序循环接收用户输入的选手数量 b,随后逐个录入编号和姓名。此步骤仅完成基础档案建立,暂不涉及分数。
第3步:评委打分与极值剔除
这是系统的核心算法部分,分为两步逻辑:
- 录入打分:
singer_score函数遍历所有选手,逐个接收1-10号评委的评分,并存入score数组,同时累加得到原始sum。 - 计算有效分:
singer_grade函数遍历每位选手的成绩数组,找出max(最高分)和min(最低分)。计算公式为:平均分 = (原始总分 - 最高分 - 最低分) / 8.0。
第4步:成绩排序与排名
order 函数通过嵌套循环比较各位选手的 aver(平均分),计算其名次 ord。最终输出时,按照排名顺序打印选手姓名、名次及得分。
第5步:数据存储
利用标准库 stdio.h 中的文件指针 FILE *fp,以追加或写入模式打开 歌手.txt 文件。遍历数组,使用 fprintf 将关键数据格式化写入磁盘,实现数据的持久化保存。
程序整体功能模块的思维导图,涵盖录入、打分、排序、查询及文件存取。
四、 C语言完整源码
以下是整合后的完整源代码。请注意,原始代码中部分 `scanf` 格式符存在不匹配风险(如 float 类型误用 %d),实际编译时建议核对参数类型以确保稳定运行。
#include<stdio.h>
#include<stdlib.h>
/* 定义歌手结构体 */
struct singer {
int num;
char name[10];
float score[20]; /* 预留空间存储评委打分 */
float aver;
float sum;
int ord;
};
struct singer singers[100]; /* 最大支持100名选手 */
/* 输入选手基本信息 */
void singer_information(int b) {
int i;
for(i=1; i<=b; i++) {
printf("请输入选手编号:");
scanf("%d", &singers[i].num);
printf("请输入选手姓名:");
scanf("%s", singers[i].name); /* 修正:数组名无需取地址 */
}
}
/* 输入评委打分并计算原始总分 */
int singer_score(int b) {
int j, c;
for(j=1; j<=b; j++) {
for(c=1; c<=10; c++) {
printf("请输入评委%d对歌手%s的打分: ", c, singers[j].name);
scanf("%f", &singers[j].score[c]);
}
/* 初始计算原始总和 */
singers[j].sum = 0;
for(c=1; c<=10; c++) {
singers[j].sum += singers[j].score[c];
}
}
return 0;
}
/* 去除最高/最低分,计算平均分 */
int singer_grade(int b) {
int a, c;
float max, min;
for(a=1; a<=b; a++) {
max = singers[a].score[1];
min = singers[a].score[1];
/* 遍历找极值 */
for(c=1; c<=10; c++) {
if(max < singers[a].score[c]) max = singers[a].score[c];
if(min > singers[a].score[c]) min = singers[a].score[c];
}
/* 计算平均分:(总分 - 最高 - 最低) / 8 */
singers[a].aver = (singers[a].sum - max - min) / 8.0;
}
return 0;
}
/* 计算排名并输出结果 */
int order(int b) {
int i, j, t;
/* 计算每个人的名次 */
for(i=1; i<=b; i++) {
t = 1;
for(j=1; j<=b; j++) {
/* 如果他人分数高于当前人,名次后退 */
if(singers[i].aver < singers[j].aver) {
t++;
}
}
singers[i].ord = t;
}
/* 按名次输出 */
printf("\n--- 比赛成绩排名 ---\n");
for(i=1; i<=b; i++) {
for(j=1; j<=b; j++) {
if(singers[j].ord == i) {
printf("姓名:%s | 编号:%d | 平均分:%.2f | 排名第:%d\n",
singers[j].name, singers[j].num, singers[j].aver, singers[j].ord);
}
}
}
printf("----------------------\n");
return 0;
}
/* 数据查询 */
int search(int c, int b) {
if(c < 1 || c > b) {
printf("无效编号!\n");
return -1;
}
printf("所查选手数据:\n");
printf("编号: %d | 姓名: %s | 平均分: %.2f | 总分: %.2f\n",
singers[c].num, singers[c].name, singers[c].aver, singers[c].sum);
return 0;
}
/* 主函数 */
int main() {
int a, b, c, n=1;
FILE *fp;
/* 初始化文件 */
fp = fopen("singer_results.txt", "w+");
if(fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
printf(" *** 歌手比赛评分系统 *** \n");
while(n) {
printf("\n*********************\n");
printf("* 1) 录入选手数据 *\n");
printf("* 2) 评委打分 *\n");
printf("* 3) 成绩排名 *\n");
printf("* 4) 数据查询 *\n");
printf("* 5) 保存文件 *\n");
printf("* 6) 退出系统 *\n");
printf("*********************\n");
printf("请选择操作: ");
scanf("%d", &a);
switch(a) {
case 1:
printf("请输入参赛选手人数: ");
scanf("%d", &b);
singer_information(b);
break;
case 2:
if(b == 0) { printf("请先录入选手数据!\n"); break; }
singer_score(b);
break;
case 3:
if(b == 0) { printf("无数据!\n"); break; }
singer_grade(b);
order(b);
break;
case 4:
if(b == 0) { printf("无数据!\n"); break; }
printf("请输入要查询的选手编号: ");
scanf("%d", &c);
search(c, b);
break;
case 5:
printf("正在写入文件...\n");
rewind(fp); /* 回到文件头重写 */
for(int i=1; i<=b; i++) {
fprintf(fp, "%d %s %.2f %.2f\n",
singers[i].num, singers[i].name,
singers[i].aver, singers[i].sum);
}
fclose(fp);
printf("数据已保存至 singer_results.txt\n");
/* 重新打开以便下次写入 */
fp = fopen("singer_results.txt", "w+");
break;
case 6:
exit(0);
default:
printf("输入错误,请重试!\n");
}
}
fclose(fp);
return 0;
}五、 关键点说明
- 数组索引:C语言数组从0开始,但本例为了贴近业务逻辑(第1位选手),在逻辑上使用1-based索引,需注意边界检查。
- 浮点数精度:成绩涉及小数,务必使用
float或double类型,并在scanf/printf中使用%f。 - 输入缓冲:混合使用
scanf读取数字和字符串时,缓冲区残留的回车符可能会干扰后续scanf("%s")的执行,实际开发中建议在读取字符串前清空缓冲区或使用吸收回车。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









