C语言实现一个简单的多功能学生成绩表格管理程序(def c++版 附原码和使用教程)

        题目:有若干由学生的姓名,学号,两门科目分数等组成的基本信息文件,使用C语言编写一个程序,可以对其进行成绩的修改和按总分高低或者学号进行排列输出,并将输出的新信息储存在新的文件中。

        开头先嗦几句,在校带学生不懂,写着玩的,如果有觉得哪里写的不好,欢迎大佬们交流讨论鸭😀😀,也欢迎白嫖和提问,文章末尾放源码和食用指南!

先看效果:

866a6cd24c554cca9483f83d7d1e00f8.jpeg 

基本思路:把一个包含学生信息的文件读取出来并写入结构体当中,按照题目要求修改完成后再写入一个新的文件中。

分析:题目给了一个包含学生姓名、学号、两门成绩[2]的学生基本信息,有多少学生咱不清楚,那我们可以先定义一个关于这些信息的结构体写在开头!

#define SC 2 //两门成绩,这里写2
#define STU 9 //有9个学生这里写9,如果要添加或减少一些学生就写多少,以此类推 

struct stuData{
	int num;//学号 
	char name[20];//姓名 
	int score[SC];//分数 
	
};//学生 

我们就用常量 STU 来代表有9个学生吧,如果后续要改动,直接在开头修改STU的值就好了,以及2门科目用SC表示,然后这个结构体中包含了学生的基本信息,比如学号(num)、姓名(name[20])以及两门成绩(score[SC])。

 

我们可以先在记事本中新建一个关于9个学生的基本信息,可以随便写一些,按照学号、姓名、两科分数每个信息中间加空格区分,不同学生信息按回车区分。以这样形式写出并记住这个路径,在文件打开读写的时候用得到。

9ffe4d9ecf6b4421b412c05d2d772807.png de633a78599344948191e2de8ad0836f.png

 这个文件路径如下:E:\CFileText\StudentData\StuData.txt 里面的信息可以随便写,但每个学生信息的格式要保持不变。可以看到,我在 E:\CFileText\StudentData 下创建了一个叫 StuData.txt 的文件,文件内包含的信息是:

10101 wang 74 62
......
10109 wei 74 58

这里可以根据个人喜好自己写了

       OK,基本的文件信息已经完成了,我们就可以在main函数中对此文件进行调用,并将他储存到我们的结构体当中,这样我们就可以先对其进行精确的操作了。代码写在main()函数中:

FILE *fp1,*fp2;//一读,一写 
	int i,j;//循环变量,后面用得到
	struct stuData st[STU];//STU个学生 
	
	if((fp1=fopen("E:\\CFileText\\StudentData\\StuData\\stuData.txt","r"))==NULL)
	{
		printf("现在找不到此文件!\n");//读不到时输出
		return 0;
	}
	if((fp2=fopen("E:\\CFileText\\StudentData\\StuData\\stuDataNew.txt","w"))==NULL)
	{
		printf("现在找不到此文件!\n");//写不了时输出
		return 0;
	}

	printf("学生分数如下:\nNumber\tName\n"); 
	for(i=0;i<STU;i++)
	{
		fscanf(fp1,"%d%s%d%d",
		&st[i].num,
		st[i].name,
		&st[i].score[0],
		&st[i].score[1]);//读取fp1指向的文件的信息并赋值给struct stu 当中 

	}
	
	for(i=0;i<STU;i++) //输出学生成绩 
	printf("%d\t%s\t%d\t%d\n",
		st[i].num,
		st[i].name,
		st[i].score[0],
		st[i].score[1]);

注意!注意!这里说一个可能有人会注意不到的点,虽然文件路径是这样的E:\CFileText\StudentData\StuData.txt

但是想要读取这个文件,那么在fopen里,要在 \ 上再加一个 \ 变成 \\ 充当 \ 的转义字符,表示那些不能直接显示的ASCII字符,因此在c语言中要想精确定位路径,fopen函数应该这样写:

fopen("E:\\CFileText\\StudentData\\StuData\\stuData.txt","r"); 

这样写是错误的:fopen("E:\CFileText\StudentData\StuData\stuData.txt","r"); 

其中文件指针*fp1将学生基本信息读取出来,这里用了一个fscanf函数读出文件信息,储存到新定义的结构体数组st[i]当中,记住这个st[]数组,后面要经常调用,而*fp2则将结构体的信息写入新文件中(现在用不上),此时直接输出结构体st[i]内容的结果如下:

14830e16a9964c60aa599b00663eab41.png

 这是按学号储存在结构体里了,但我们想要的需求是按照两科成绩的总分相加从大到小的进行输出再储存在新文件中,还要可以修改其中一人的成绩,但有没有好的工具能让我们分别做这几件事又不会互相干扰呢?

7c647fccc4c84962b77aa67245d1f13b.png

我的解法是:while循环内套一个switch,这样我们既可以修改单个学生的成绩,又可以以学号或者排名输出学生信息列表,直到我们处理完成后,输入结束的指令这样就可以跳出循环了。

由于学号和成绩都是int类型,因此,按学号和按成绩输出排名的本质上都是int类型的冒泡排序或者其他擂台排序之类的算法,这里不过多介绍,可以参考一些冒泡排序算法。

交换结构体的临时变量temp如下:

struct stuData temp;
	       	 temp=st[j+1];
	       	 st[j+1]=st[j];
	       	 st[j]=temp;

 当(st[j].score[0]+st[j].score[0])<(st[j+1].score[0]+st[j+1].score[0])时,也就是后一位学生成绩大于前一位学生成绩时,让两者交换在结构体当中的位置。采用临时结构体变量temp进行临时储存一者的变量,并赋给另外一者。

详细代码和输出结果如下:

case 1:{
		//按两科总成绩输出学生排名 
		int max,j1;
		for(i=0;i<STU;i++)//擂台排序 
		{
			max=st[i].score[0]+st[i].score[1];
			j1=i;
			for(j=i+1;j<STU;j++)
		   {
		 	  if(max<(st[j].score[0]+st[j].score[1]))
		 	  {
		 	  	j1=j;
		 	  	max=(st[j].score[0]+st[j].score[1]);
			  }
	       }
	       
	       if((st[j1].score[0]+st[j1].score[1])>(st[i].score[0]+st[i].score[1]))//确认排序的正确性的多一份保险,这行可以直接省略 
	       {
	       	 struct stuData temp;
	       	 temp=st[j1];
	       	 st[j1]=st[i];
	       	 st[i]=temp;
		   }
		}
		printf("学生排名:\n");//输出学生排名 
		for(i=0;i<STU;i++)
		printf("%d\t%s\t%d\t%d\n",
		st[i].num,
		st[i].name,
		st[i].score[0],
		st[i].score[1]);
		
		break;
	}

3ed8ac7db9e8450eb25678e833aa4ecb.png

OK,接下来处理修改学生成绩的算法,以学号输出学生排名和以成绩输出排名算法基本上大同小异,这里不过多介绍,修改学生成绩思路是先要定位学生学号,确定修改的是科目1还是科目2,修改后储存到结构体数组st[i]中,再对其进行输出,确保我们的修改符合预期,算法和程序运行结果如下:

	case 2:{
		//改变单个学生的成绩
		while(1)
		{
		int numb=-1;//存学号的临时变量 
		printf("\n请输入你想要更改成绩的学生的学号,按0结束:\n");
		scanf("%d",&numb);//输入想改成绩的学生学号 
		
		getchar();//消化掉输入的字符 
		
		if(numb==-1)
		{
			printf("学号错误,请再输入一次\n");//numb没有发生改变 
			continue;
		}
		if(numb==0)//结束 
		break;
		
		j=-1;//搜寻学号的指示器 
		  for(i=0;i<STU;i++)
		  {
			if(st[i].num!=numb)
			continue;
			
			else if(st[i].num==numb)
			{
				j=i;//找到了指定学号 
				break; 
			}
		  }
		  
		  if(j==-1)//未找到指定学号
		  {
		  	printf("找不到此学号,要不再试试?\n");
		  	continue;
		  }
		  else{
		printf("当前学生的信息:\n%d\t%s\t%d\t%d\n",
		st[j].num,
		st[j].name,
		st[j].score[0],
		st[j].score[1]);//输出当前该学生信息 
		
		int newScore;
		
		printf("你想要更改的科目是1还是2?\n(1  \\  2)\n");
		scanf("%d",&i);
		i--;
		//getchar();
		if(i!=1&&i!=0)
		continue;
		
		printf("\n%d ---> ",st[j].score[i]);//输入分数直接修改原学生成绩 
		scanf("%d",&newScore);
		st[j].score[i]=newScore;
		
		printf("信息更新:\n%d\t%s\t%d\t%d\n",
		st[j].num,
		st[j].name,
		st[j].score[0],
		st[j].score[1]);//输出学生列表 
		
		printf("\n还要继续修改吗?\n(1 \\ 0)\n");
		scanf("%d",&newScore);
		if(newScore==0)
		break;
		  }
		  
		  
		}
		
		break;
	}

52a8d6f2b9e24dcc807844753fffcb3b.png

上述中,我把yang同学的50分,捞成了63分并储存了起来,通过一个while循环可以反复这样以此类推的操作。按照以上基本思路完成修改、排列的操作后,我们可以把st[i]中储存的学生信息再通过*fp2文件指针输出到新的文件当中,这样就算是完成题目要求了。

输出新文件的算法和结果如下:

int q=-1;//q作为指示器 
	printf("要保存在新文件夹里吗?\n(1 \\ 0)\n");
	scanf("%d",&q);
	if(q==1)
	{
		for(i=0;i<STU;i++)
		{
			fprintf(fp2,"%d\t%s\t%d\t%d\n",st[i].num,st[i].name,st[i].score[0],st[i].score[1]);//把修改过的学生表写入新文件中 
		}
		printf("\n保存成功!");
	}
	if(q==-1){
		printf("\n感谢您的使用,再见!");
		return 0;
	}
	printf("\n感谢您的使用!再见!");
	fclose(fp1);//关闭fp指针 
	fclose(fp2);
	return 0;

64939e85b0f44e02bc9e5ee2d4faace2.png

这里用了一个fprint函数,用于直接连带数组信息和换行以及tap美化输出到新文件中,使用文件结束后使用fclose关闭俩新旧文件。

新旧文件变化如下(左: 旧文件; 右: 按照总分排名输出的新文件):

c8bc78042d464ccda4a602c0271861fa.png

 以上就是全部内容了,接下来我把源码、学生文件和食用指南放在下面,感兴趣的可以运行试试看,如果有其他建议的也欢迎交流讨论。

源码、文件如下:

#include <stdio.h>
#include <stdlib.h>

#define SC 2 //两门成绩,这里写2,这个改掉对后面代码会有影响,建议别改 
#define STU 9 //有9个学生这里写9,如果要添加或减少一些学生就写多少,以此类推 

struct stuData{
	int num;//学号 
	char name[20];//姓名 
	int score[SC];//分数 
	
};//学生 


int main() 
{
	FILE *fp1,*fp2;//一读,一写 
	int i,j;//循环变量
	struct stuData st[STU];//STU个学生 
	
	if((fp1=fopen("E:\\CFileText\\StudentData\\StuData\\stuData.txt","r"))==NULL)
	{
		printf("现在找不到此文件!\n");//读 
		return 0;
	}
	if((fp2=fopen("E:\\CFileText\\StudentData\\StuData\\stuDataNew.txt","w"))==NULL)
	{
		printf("现在找不到此文件!\n");//写 
		return 0;
	}
	
	rewind(fp1);//使fp1重新指向开头,这句可删 
	
	printf("学生分数如下:\nNumber\tName\n"); 
	for(i=0;i<STU;i++)
	{
		fscanf(fp1,"%d%s%d%d",
		&st[i].num,
		st[i].name,
		&st[i].score[0],
		&st[i].score[1]);//读取fp1指向的文件的信息并赋值给struct stu 当中 

	}
	
	for(i=0;i<STU;i++) //输出学生成绩 
	printf("%d\t%s\t%d\t%d\n",
		st[i].num,
		st[i].name,
		st[i].score[0],
		st[i].score[1]);
	
	printf("请输入您的操作\n"
	"按1以科目总成绩排名输出学生排名\n"
	"按2修改学生成绩\n"
	"按3以学号顺序输出学生排名\n");//操作改成绩,输入成绩,排名等 
	
	int q=-1;//指示器 //输入1,2,3以确定下一步操作 
	while(1)
	{
	  scanf("%d",&q);
	  if((q==-1)&&(q!=1)&&(q!=2)&&(q!=3))//输入合法检测 
	  {
		printf("你的输入有错\n请重新输入:");
		getchar();
		continue;
	  }
	  else break;
	}
	
	
while(1)
{
	int s1=-1;	//结束循环指示器 
	
	switch(q)
	{
	//按两科总成绩输出学生排名 
	case 1:{
		//按两科总成绩输出学生排名 
		int max,j1;
		for(i=0;i<STU;i++)//擂台排序 
		{
			max=st[i].score[0]+st[i].score[1];
			j1=i;
			for(j=i+1;j<STU;j++)
		   {
		 	  if(max<(st[j].score[0]+st[j].score[1]))
		 	  {
		 	  	j1=j;
		 	  	max=(st[j].score[0]+st[j].score[1]);
			  }
	       }
	       
	       if((st[j1].score[0]+st[j1].score[1])>(st[i].score[0]+st[i].score[1]))//确认排序的正确性的多一份保险,这行可以直接省略 
	       {
	       	 struct stuData temp;
	       	 temp=st[j1];
	       	 st[j1]=st[i];
	       	 st[i]=temp;
		   }
		}
		printf("学生排名:\n");//输出学生排名 
		for(i=0;i<STU;i++)
		printf("%d\t%s\t%d\t%d\n",
		st[i].num,
		st[i].name,
		st[i].score[0],
		st[i].score[1]);
		
		break;
	}
	//改变单个学生的成绩
	case 2:{
		//改变单个学生的成绩
		while(1)
		{
		int numb=-1;//存学号的临时变量 
		printf("\n请输入你想要更改成绩的学生的学号,按0结束:\n");
		scanf("%d",&numb);//输入想改成绩的学生学号 
		
		getchar();//消化掉输入的字符 
		
		if(numb==-1)
		{
			printf("学号错误,请再输入一次\n");//numb没有发生改变 
			continue;
		}
		if(numb==0)//结束 
		break;
		
		j=-1;//搜寻学号的指示器 
		  for(i=0;i<STU;i++)
		  {
			if(st[i].num!=numb)
			continue;
			
			else if(st[i].num==numb)
			{
				j=i;//找到了指定学号 
				break; 
			}
		  }
		  
		  if(j==-1)//未找到指定学号
		  {
		  	printf("找不到此学号,要不再试试?\n");
		  	continue;
		  }
		  else{
		printf("当前学生的信息:\n%d\t%s\t%d\t%d\n",
		st[j].num,
		st[j].name,
		st[j].score[0],
		st[j].score[1]);//输出当前该学生信息 
		
		int newScore;
		
		printf("你想要更改的科目是1还是2?\n(1  \\  2)\n");
		scanf("%d",&i);
		i--;
		//getchar();
		if(i!=1&&i!=0)
		continue;
		
		printf("\n%d ---> ",st[j].score[i]);//输入分数直接修改原学生成绩 
		scanf("%d",&newScore);
		st[j].score[i]=newScore;
		
		printf("信息更新:\n%d\t%s\t%d\t%d\n",
		st[j].num,
		st[j].name,
		st[j].score[0],
		st[j].score[1]);//输出学生列表 
		
		printf("\n还要继续修改吗?\n(1 \\ 0)\n");
		scanf("%d",&newScore);
		if(newScore==0)
		break;
		  }
		  
		  
		}
		
		break;
	}
	//按学号输出学生排名
	case 3:{
	//按学号输出学生排名 
		int j1,min;
		for(i=0;i<STU;i++)//擂台排序 
		{
			j1=i;
			min=st[i].num;
			for(j=i+1;j<STU;j++)
		   {
		 	  if(min>st[j].num)
		 	  {
		 	  	j1=j;
		 	  	min=st[j].num;
			  }
	       }
	       
	       if(st[i].num>st[j1].num)//确认排序的正确性的多一份保险,其实这行可以直接省略 
	       {
	       	 struct stuData temp;
	       	 temp=st[j1];
	       	 st[j1]=st[i];
	       	 st[i]=temp;
		   }
		}
		printf("学生排名:\n");
		for(i=0;i<STU;i++)
		printf("%d\t%s\t%d\t%d\n",
		st[i].num,
		st[i].name,
		st[i].score[0],
		st[i].score[1]);
		
		break;
	}
	default:break;	
	}
	printf("\n\n现在结束吗\n(1 \\ 0)\n");
	scanf("%d",&s1);//用s1作为检测结束信号 
	if(s1==-1)//检测s1是否被赋值 
	  {
		getchar();//消化掉可能输入的字符 
		break;
	  }
	
	 
	else if(s1==1)//1结束 
	break;
	
	else if(s1==0){//0继续修改 
	printf("请输入您的操作\n"
	"按1以科目总成绩排名输出学生排名\n"
	"按2修改学生成绩\n"
	"按3以学号顺序输出学生排名\n");//操作改成绩,输入成绩,排名等 
	scanf("%d",&q);//输入1,2,3以确定下一步操作 
	continue;
	}
	
}//结束了全部循环
 
	for(i=0;i<STU;i++)//输出此时的学生列表新信息 
		printf("%d\t%s\t%d\t%d\n",
		st[i].num,
		st[i].name,
		st[i].score[0],
		st[i].score[1]);
	
	q=-1;//q作为指示器 
	printf("要保存在新文件夹里吗?\n(1 \\ 0)\n");
	scanf("%d",&q);
	if(q==1)
	{
		for(i=0;i<STU;i++)
		{
			fprintf(fp2,"%d\t%s\t%d\t%d\n",st[i].num,st[i].name,st[i].score[0],st[i].score[1]);//把修改过的学生表写入新文件中 
		}
		printf("\n保存成功!");
	}
	if(q==-1){
		printf("\n感谢您的使用,再见!");
		return 0;
	}
	printf("\n感谢您的使用!再见!");
	fclose(fp1);//关闭fp指针 			
	fclose(fp2);
	return 0;
}

文件名:stuData.txt

内容如下:

10101 wang 74 62
10102 li 56 81
10103 zhao 70 56
10104 yang 60 50
10105 qi 55 77
10106 huang 92 85
10107 zheng 88 63
10108 tang 54 64
10109 wei 74 58

储存位置:E:\CFileText\StudentData\StuData

使用方法:

首先要在你的电脑中一个位置,储存一个.txt的文件,文件名和内容如上。

然后在下面这段代码的fopen函数中(只位于main函数开头),使文件指针fp指向该位置,注意!要使用 \\ 双斜杠来区分不同文件夹,而不是 \ 单斜杠。fopen函数中,"r"表示读,"w"表示写。

ce059e4b7e4f4fcaab2fcd969490abf1.png

 最后,在开头STU当中,如果后续学生文件表里添加新的学生信息,只需要改动STU就可以了,比如有8个学生,我STU常数就改成8,有9个就改成9,以此类推,自由度很高。

但是SC改动的话并不会生效,这个要改的话后面还要改一大堆东西,不建议改,一改全体生效的这个说实话我做不到力(悲)

24b3a84dc61a41a883eac2746c1bea31.png

处理完成STU和文件位置,这一大段代码就可以直接用了,有疑问的可以发在评论区,咱友好讨论鸭😀😀

 

  • 5
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值