前言
今天在整理文件时找到几个以前写的C语言项目,终于知道程序员为什么讨厌看自己远古时期写的代码了🤣。决定拿出来分享一下!
项目简介
说明
- 这是一个纯C语言实现的学生信息管理系统,以本人一年后的编程习惯来看,有些不尽人意,所以此博客在分享代码的同时会指出其中可改进之处。
- 此外,作者原本使用的是macOS系统,需要自己实现“getch()”函数,当时能力有限,于是在Windows虚拟机上的写的代码,在博客最后面贴了在Linux系统中实现getch函数的代码。
getch(): 1.默认不回显输入的数据,在输入管理员密码时可以自定义回显;2.吃掉回车(清空缓冲区) - 所以,作者提供的代码在Windows上可以直接使用,在Linux或macOS上则需要改动(参考后文)。
程序示意
初始菜单
主菜单
程序代码
/*
* Copyright © 2018 WUST All rights reserved.
*
* filename : 学生档案win/1.cpp
* filepath :
* instruction :
* function :
*
* Author : Mr.Killer
* Date : 2018-12-15 23:11:50
*/
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <string.h>
#include <conio.h>
int choice1, choice2;
int SN1;
int SN2;
int check1(int choice1); // 主界面1的选择处理
int check2(); // 检查核对管理员密码
void check3(); // 主界面2的选择处理
void draw1(); // 主界面1
void draw2(); // 主界面2
void bui(int i, FILE *fb); // 创建学生档案
void add(int i, FILE *fa); // 添加学生信息
int edi(); // 编辑信息
int del(); // 删除信息
int fin(); // 搜索信息
void gla(); // 遍历输出档案
void ran(); // 档案排序
void sta(); // 成绩统计
struct stu
{
int number;
char name[15];
float score[4];
} stu, stus[31]; // stu用于文件读写,stus[31]用于排序
int main(int argc, char const *argv[])
{
while (1)
{
FILE *S = fopen("学生档案附属.txt", "r");
fscanf(S, "%d %d", &SN1, &SN2);
fclose(S);
draw1();
int choice1;
scanf("%d", &choice1);
int flag = check1(choice1);
if (flag)
break;
}
return 0;
}
void draw1() //主界面1
{
system("cls");
printf("|-----------------------------------------------------------------------|\n");
printf("| |\n");
printf("| 欢迎使用学生档案系统 |\n");
printf("| (请输入0~3) |\n");
printf("|-----------------------------------------------------------------------|\n");
printf("| |\n");
printf("| |\n");
printf("| |\n");
printf("| |\n");
printf("| 1--档案管理 |\n");
printf("| 2--查找学生 |\n");
printf("| 3--浏览成绩单 |\n");
printf("| 0--退出 |\n");
printf("| |\n");
printf("| |\n");
printf("| |\n");
printf("|-----------------------------------------------------------------------|\n");
printf(" 请选择:");
}
void draw2() //主界面2
{
system("cls"); //system()函数的功能是执行系统命令,cls是清屏指令
printf("|-----------------------------------------------------------------------|\n");
printf("| |\n");
printf("| 请输入选项编号 |\n");
printf("| (0~8) |\n");
printf("|-----------------------------------------------------------------------|\n");
printf("| |\n");
printf("| 1--创建成绩单 |\n");
printf("| 2--添加学生 |\n");
printf("| 3--编辑学生 |\n");
printf("| 4--删除学生 |\n");
printf("| 5--查找学生 |\n");
printf("| 6--浏览成绩单 |\n");
printf("| 7--排序成绩单 |\n");
printf("| 8--统计成绩 |\n");
printf("| 0--退出 |\n");
printf("| |\n");
printf("|-----------------------------------------------------------------------|\n");
printf(" 请选择:");
}
int check1(int choice1) // 主界面1的选择处理
{
int flag = 1;
if (choice1 == 1)
{
flag = 0;
int result = check2();
if (result == 0)
check3();
else
{
flag = 1;
printf("密码输入错误,退出程序!\n");
return flag;
}
}
else if (choice1 == 2)
{
flag = 0;
fin();
printf("输入任意键继续:");
getch();
}
else if (choice1 == 3)
{
flag = 0;
gla();
printf("输入任意键继续:");
getch();
}
else
return flag;
}
int check2() // 检查核对管理员密码
{
printf("请输入管理员密码:");
char get[15];
int i = 0;
while (1)
{
get[i] = getch();
if (get[i] == '\r')
break;
printf("*");
i++;
}
get[i] = '\0';
int result = strcmp("Irvingsoft", get);
return result;
}
void check3() // 主界面2的选择处理
{
printf("输入密码成功!\n");
loop:
draw2();
int choice;
scanf("%d", &choice);
switch (choice)
{
case 1:
{
printf("您选择了\"1--创建成绩单\"\n");
printf("请输入学生人数:");
int n;
scanf("%d", &n);
if (n >= 1 && n <= 30)
{
FILE *fb = fopen("学生档案.txt", "wb");
if (fb == NULL)
{
printf("档案创建失败!\n即将返回上一级!\n");
return;
}
else
{
for (int i = 0; i < n; i++)
{
bui(i, fb);
SN1++;
}
fclose(fb);
FILE *S = fopen("学生档案附属.txt", "w");
int temp;
fscanf(S, "%d %d", &temp, &SN2);
fprintf(S, "%d %d", SN1, SN2);
fclose(S);
printf("成绩单创建成功!\n");
printf("输入任意键继续:");
getch();
goto loop;
}
}
else
printf("输入错误.\n");
}
break;
case 2:
{
printf("您选择了\"2--添加学生\"\n");
printf("请输入添加学生人数:\n");
int n;
scanf("%d", &n);
if (n >= 1)
{
FILE *fa = fopen("学生档案.txt", "ab");
for (int i = 0; i < n; i++)
{
add(i, fa);
SN1++;
}
fclose(fa);
FILE *S = fopen("学生档案附属.txt", "w");
int temp;
fscanf(S, "%d %d", &temp, &SN2);
fprintf(S, "%d %d", SN1, SN2);
fclose(S);
printf("添加成功!\n");
printf("输入任意键继续:");
getch();
goto loop;
}
else
printf("输入错误!\n");
}
break;
case 3:
{
printf("您选择了\"3--编辑学生\"\n");
int flag = edi();
if (flag)
goto loop;
printf("编辑成功!\n");
printf("输入任意键继续:");
getch();
goto loop;
}
break;
case 4:
{
printf("您选择了\"4--删除学生\"\n");
int flag = del();
if (flag)
goto loop;
SN2++;
FILE *S = fopen("学生档案附属.txt", "w");
int temp;
fscanf(S, "%d %d", &SN1, &temp);
fprintf(S, "%d %d", SN1, SN2);
fclose(S);
printf("删除成功!\n");
fflush(stdin);
printf("输入任意键继续:");
getch();
goto loop;
}
break;
case 5:
{
printf("您选择了\"5--查找学生\"\n");
int flag = fin();
if (flag)
goto loop;
printf("输入任意键继续:");
getch();
goto loop;
}
break;
case 6:
{
gla();
printf("输入任意键继续:");
getch();
goto loop;
}
break;
case 7:
{
printf("您选择了\"7--排序成绩单\"\n");
ran();
printf("输入任意键继续:");
getch();
goto loop;
}
break;
case 8:
{
sta();
printf("输入任意键继续:");
getch();
goto loop;
}
break;
case 0:
break;
}
}
void bui(int i, FILE *fb) // 创建学生档案
{
printf("请输入第%2d条记录\n", i + 1);
printf("请输入学号:");
int number;
loop1:
getchar();
scanf("%d", &number);
if (number <= 0)
{
printf("格式输入错误,请重新输入:");
goto loop1;
}
FILE *ff = fopen("学生档案.txt", "rb");
if (ff != NULL)
{
for (int i = 0; i < SN1; i++)
{
fread(&stu, sizeof(stu), 1, ff);
if (stu.number == number)
{
printf("输入学号重复,请重新输入:");
goto loop1;
}
}
fclose(ff);
}
else
{
printf("文件读取失败!\n即将返回\n");
Sleep(2000);
return;
}
stu.number = number;
getchar();
printf("请输入姓名:");
gets(stu.name);
//fflush(stdin); // 清空键盘缓冲区
printf("请输入数学成绩:");
scanf("%f", &stu.score[0]);
printf("请输入语文成绩:");
scanf("%f", &stu.score[1]);
printf("请输入英语成绩:");
scanf("%f", &stu.score[2]);
stu.score[3] = (stu.score[0] + stu.score[1] + stu.score[2]) / 3;
fwrite(&stu, sizeof(stu), 1, fb);
}
void add(int i, FILE *fa) // 添加学生信息
{
printf("请输入第%2d条记录\n", i + 1);
printf("请输入学号:");
int number;
loop1:
getchar();
scanf("%d", &number);
if (number <= 0)
{
printf("格式输入错误,请重新输入:");
goto loop1;
}
FILE *ff = fopen("学生档案.txt", "rb");
if (ff != NULL)
{
for (int i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, ff);
if (stu.number == number)
{
printf("输入学号重复,请重新输入:");
goto loop1;
}
}
fclose(ff);
}
else
{
printf("文件读取失败!\n即将返回\n");
Sleep(2000);
return;
}
stu.number = number;
getchar();
printf("请输入姓名:");
gets(stu.name);
//fflush(stdin); //清空键盘缓冲区
printf("请输入数学成绩:");
scanf("%f", &stu.score[0]);
printf("请输入语文成绩:");
scanf("%f", &stu.score[1]);
printf("请输入英语成绩:");
scanf("%f", &stu.score[2]);
stu.score[3] = (stu.score[0] + stu.score[1] + stu.score[2]) / 3;
fseek(fa, 0, SEEK_END);
fwrite(&stu, sizeof(stu), 1, fa);
}
int edi() // 编辑信息
{
loop2:
printf("请输入学号:");
int number;
getchar();
scanf("%d", &number);
if (number <= 0)
{
printf("格式输入错误,");
goto loop2;
}
int flag = 1;
FILE *ff = fopen("学生档案.txt", "rb");
for (int i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, ff);
if (stu.number == number)
{
loop1:
FILE *fe = fopen("学生档案.txt", "r+b");
fseek(fe, i * sizeof(stu), SEEK_SET);
fread(&stu, sizeof(stu), 1, fe);
fseek(fe, i * sizeof(stu), SEEK_SET);
flag = 0;
system("cls");
printf("|-----------------------------------------------------------------------|\n");
printf("| |\n");
printf("| 编辑学生 |\n");
printf("| (请输入0~5) |\n");
printf("|-----------------------------------------------------------------------|\n");
printf("| |\n");
printf("| |\n");
printf("| |\n");
printf("| 1--学号 |\n");
printf("| 2--姓名 |\n");
printf("| 3--数学 |\n");
printf("| 4--语文 |\n");
printf("| 5--英语 |\n");
printf("| 0--退出 |\n");
printf("| |\n");
printf("| |\n");
printf("|-----------------------------------------------------------------------|\n");
printf(" 请选择:");
int choice1, choice2;
scanf("%d", &choice1);
if (choice1 == 1)
{
printf("请输入新学号:");
scanf("%d", &stu.number);
fwrite(&stu, sizeof(stu), 1, fe);
fclose(fe);
printf("修改学号成功!\n");
printf("1--继续修改\n");
printf("2--切换学生\n");
printf("0--退出\n");
scanf("%d", &choice2);
if (choice2 == 1)
{
choice2 = 0;
goto loop1;
}
else if (choice2 == 2)
{
choice2 = 0;
goto loop2;
}
else
{
choice2 = 0;
break;
}
}
else if (choice1 == 2)
{
printf("请输入新姓名:");
getchar();
gets(stu.name);
fwrite(&stu, sizeof(stu), 1, fe);
fclose(fe);
printf("修改姓名成功!\n");
printf("1--继续修改\n");
printf("2--切换学生\n");
printf("0--退出\n");
scanf("%d", &choice2);
if (choice2 == 1)
{
choice2 = 0;
goto loop1;
}
else if (choice2 == 2)
{
choice2 = 0;
goto loop2;
}
else
{
choice2 = 0;
break;
}
}
else if (choice1 == 3)
{
printf("请输入新数学成绩:");
scanf("%f", &stu.score[0]);
stu.score[3] = (stu.score[0] + stu.score[1] + stu.score[2]) / 3;
fwrite(&stu, sizeof(stu), 1, fe);
fclose(fe);
printf("修改数学成绩成功!\n");
printf("1--继续修改\n");
printf("2--切换学生\n");
printf("0--退出\n");
scanf("%d", &choice2);
if (choice2 == 1)
{
choice2 = 0;
goto loop1;
}
else if (choice2 == 2)
{
choice2 = 0;
goto loop2;
}
else
{
choice2 = 0;
break;
}
}
else if (choice1 == 4)
{
printf("请输入新语文成绩:");
scanf("%f", &stu.score[1]);
stu.score[3] = (stu.score[0] + stu.score[1] + stu.score[2]) / 3;
fwrite(&stu, sizeof(stu), 1, fe);
fclose(fe);
printf("修改语文成绩成功!\n");
printf("1--继续修改\n");
printf("2--切换学生\n");
printf("0--退出\n");
scanf("%d", &choice2);
if (choice2 == 1)
{
choice2 = 0;
goto loop1;
}
else if (choice2 == 2)
{
choice2 = 0;
goto loop2;
}
else
{
choice2 = 0;
break;
}
}
else if (choice1 == 5)
{
printf("请输入新英语成绩:");
scanf("%f", &stu.score[2]);
stu.score[3] = (stu.score[0] + stu.score[1] + stu.score[2]) / 3;
fwrite(&stu, sizeof(stu), 1, fe);
fclose(fe);
printf("修改英语成绩成功!\n");
printf("1--继续修改\n");
printf("2--切换学生\n");
printf("0--退出\n");
scanf("%d", &choice2);
if (choice2 == 1)
{
choice2 = 0;
goto loop1;
}
else if (choice2 == 2)
{
choice2 = 0;
goto loop2;
}
else
{
choice2 = 0;
break;
}
}
else
break;
}
}
fclose(ff);
if (flag)
{
printf("档案内无此学号!\n即将返回上一菜单!\n");
}
return flag;
}
int del() // 删除信息
{
printf("请输入学号:");
int number;
int flag = 1;
loop1:
getchar();
scanf("%d", &number);
if (number <= 0)
{
printf("格式输入错误,请重新输入:");
goto loop1;
}
FILE *ff = fopen("学生档案.txt", "rb");
if (ff == NULL)
{
printf("打开源文件失败!即将返回上一菜单!\n");
return 1;
}
FILE *ft = fopen("temp.txt", "a+b");
if (ft == NULL)
{
printf("打开临时文件失败!即将返回上一菜单\n");
return 1;
}
for (int i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, ff);
if (stu.number == number)
{
flag = 0;
continue;
}
else
{
//fread(&stu, sizeof(stu), 1, ft);
fwrite(&stu, sizeof(stu), 1, ft);
}
}
fclose(ft);
fclose(ff);
int r;
do
{
r = remove("学生档案.txt");
} while (r != 0);
do
{
r = rename("temp.txt", "学生档案.txt");
} while (r != 0);
if (flag)
{
printf("学号不存在!\n即将返回上一菜单!\n");
}
return flag;
}
int fin() // 搜索信息
{
printf("请输入学号:");
int number;
int flag = 1;
loop1:
getchar();
scanf("%d", &number);
if (number == 0)
{
printf("格式输入错误,请重新输入:");
goto loop1;
}
FILE *ff = fopen("学生档案.txt", "rb");
for (int i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, ff);
if (stu.number == number)
{
flag = 0;
system("cls");
printf("=====================================================\n");
printf("学号\t|姓名\t|数学\t|语文\t|英语\t|平均\t|名次\n");
printf("=====================================================\n");
printf("%d\t|%s\t|%.f\t|%.f\t|%.f\t|%.1f\t|%d", stu.number, stu.name, stu.score[0], stu.score[1], stu.score[2], stu.score[3], i + 1);
printf("\n");
fclose(ff);
break;
}
}
if (flag)
{
printf("档案内无此学号!\n即将返回上一菜单!\n");
}
return flag;
}
void gla() // 遍历输出档案
{
system("cls");
FILE *fg = fopen("学生档案.txt", "rb");
if (fg == NULL)
{
printf("打开源文件失败!即将返回上一菜单!\n");
return;
}
printf("=====================================================\n");
printf("学号\t|姓名\t|数学\t|语文\t|英语\t|平均\t|名次\n");
printf("=====================================================\n");
for (int i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, fg);
printf("%d\t|%s\t|%.f\t|%.f\t|%.f\t|%.1f\t|%d", stu.number, stu.name, stu.score[0], stu.score[1], stu.score[2], stu.score[3], i + 1);
printf("\n");
}
fclose(fg);
}
void ran() // 档案排序
{
FILE *ff = fopen("学生档案.txt", "rb");
for (int i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, ff);
stus[i].number = stu.number;
strcpy(stus[i].name, stu.name);
for (int j = 0; j < 4; j++)
stus[i].score[j] = stu.score[j];
}
fclose(ff);
for (int i = 0; i < SN1 - SN2; i++)
{
int flag = 1;
for (int j = 0; j < SN1 - SN2 - 1; j++)
if (stus[j].score[3] < stus[j + 1].score[3])
{
stus[30] = stus[j];
stus[j] = stus[j + 1];
stus[j + 1] = stus[30];
flag = 0;
}
if (flag)
break;
}
FILE *fr = fopen("temp.txt", "wb");
for (int i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, fr);
stu.number = stus[i].number;
strcpy(stu.name, stus[i].name);
for (int j = 0; j < 4; j++)
stu.score[j] = stus[i].score[j];
fseek(fr, i * sizeof(stu), SEEK_SET);
fwrite(&stu, sizeof(stu), 1, fr);
}
fclose(fr);
int r;
do
{
r = remove("学生档案.txt");
} while (r != 0);
do
{
r = rename("temp.txt", "学生档案.txt");
} while (r != 0);
printf("成绩排序成功!\n");
}
void sta() // 成绩统计
{
system("cls");
printf("您选择了\"8--统计成绩\"\n");
printf("=====================================================================\n");
printf("\t|A\t|B\t|C\t|D\t|及格数\t|及格率\n");
printf("=====================================================================\n");
int a = 0, b = 0, c = 0, d = 0, e = 0;
int i = 0;
int s = 0;
FILE *ff = fopen("学生档案.txt", "rb");
for (i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, ff);
if (stu.score[0] >= 90)
a++;
else if (stu.score[0] >= 80 && stu.score[0] <= 89)
b++;
else if (stu.score[0] >= 60 && stu.score[0] <= 79)
c++;
else
d++;
s++;
}
fseek(ff, 0, SEEK_SET);
e = a + b + c;
float f = e * 1.0 / s;
printf("数学\t|%d\t|%d\t|%d\t|%d\t|%d\t|%.2f\n", a, b, c, d, e, f);
a = 0;
b = 0;
c = 0;
d = 0;
e = 0;
s = 0;
for (i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, ff);
if (stu.score[1] >= 90)
a++;
else if (stu.score[1] >= 80 && stu.score[1] <= 89)
b++;
else if (stu.score[1] >= 60 && stu.score[1] <= 79)
c++;
else
d++;
s++;
}
fseek(ff, 0, SEEK_SET);
e = a + b + c;
f = e * 1.0 / s;
printf("语文\t|%d\t|%d\t|%d\t|%d\t|%d\t|%.2f\n", a, b, c, d, e, f);
a = 0;
b = 0;
c = 0;
d = 0;
e = 0;
s = 0;
for (i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, ff);
if (stu.score[2] >= 90)
a++;
else if (stu.score[2] >= 80 && stu.score[2] <= 89)
b++;
else if (stu.score[2] >= 60 && stu.score[2] <= 79)
c++;
else
d++;
s++;
}
fseek(ff, 0, SEEK_SET);
e = a + b + c;
f = e * 1.0 / s;
printf("英语\t|%d\t|%d\t|%d\t|%d\t|%d\t|%.2f\n", a, b, c, d, e, f);
a = 0;
b = 0;
c = 0;
d = 0;
e = 0;
s = 0;
for (i = 0; i < SN1 - SN2; i++)
{
fread(&stu, sizeof(stu), 1, ff);
if (stu.score[3] >= 90)
a++;
else if (stu.score[3] >= 80 && stu.score[3] <= 89)
b++;
else if (stu.score[3] >= 60 && stu.score[3] <= 79)
c++;
else
d++;
s++;
}
e = a + b + c;
f = e * 1.0 / s;
printf("平均\t|%d\t|%d\t|%d\t|%d\t|%d\t|%.2f\n", a, b, c, d, e, f);
}
问题分析
编码规范
- 命名规则 这a,b,c,d以及函数名属实看得头疼,下附阿里巴巴Java开发手册,变量、常量以及函数名的命名规则基本可以通用于C/C++
链接:阿里巴巴Java开发手册 密码:bpsh
程序优化
- 用枚举(enum)替换选项值,于是三个check函数可以用重载的方式实现
- 减少FILE指针的传递,通过改写check函数里的switch_case,例如check3中case 1直接调用创建档案的函数
- 三个以上的 if~else 判断结构替换成 switch_case 结构
- 用if~else或while替换 goto_loop 结构
Linux(或macOS)实现getch()函数
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
int getch()
{
int c = 0;
struct termios org_opts, new_opts;
int res = 0;
//----- store old settings -----------
res = tcgetattr(STDIN_FILENO, &org_opts);
assert(res == 0);
//---- set new terminal parms --------
memcpy(&new_opts, &org_opts, sizeof(new_opts));
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);
tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
c = getchar();
//------ restore old settings ---------
res = tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
assert(res == 0);
return c;
}
其中不含main函数,包括自定义getch所需的头文件和getch函数的定义。
如果要将代码移植到Linux或macOS系统,清屏指令应该为:
system("clear screen");
反思总结
还有值得优化的代码欢迎评论指出!!