c语言项目 — 设计到文件操作的一个评分系统

在复习链表的时候,翻回了之前做过的一个选手评分系统,顺便分享记录一下,免得以后找不到了,毕竟那是第一次做c语言项目。这篇文章可能很长,代码量1000多行,顺便说一下,下面的代码解释基本都是以注释方式解释。

        

        

目录:

        1.项目整体介绍
        2.项目功能实现
        3.项目全部代码
        4.总结项目,做出评价


一、项目整体介绍

  1. 首先该项目分为学生系统和管理员系统,用不同的账号实现登陆不同的系统。截图如下
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
  2. 说明:可以看到大概的功能情况,两个系统,管理员系统,选手系统,通过密码登陆。当然了,管理员系统有一些功能是重复了,也算是一个不好的点吧。另外,选手密码可以自己设置,因为原代码涉及个人信息,所以选手密码用了********代替。


二、项目功能介绍(选几个我当时做起来比较难实现的功能)

  1. 其实通过上面的图片大概都能猜到是什么意思,所以不再过多介绍功能的运作情况。源代码会给出到最后,如果感兴趣可以自己测试。下面会讲解管理员系统的功能的实现。


  2. 项目的登陆的实现
    在这里插入图片描述
    讲解:这里就是直接通过while(1)控制整个大循环,需要内部的break跳出。可以看到图中第一个Password是12345678,这就是管理员系统,第二个是********,也就是选手系统。在管理员系统选择退出登陆后就会退出到while(1)的界面,也就是现在的截图界面。如果想要退出while(1),可以看到的是最后也有两个选择,else if 和else控制退出while(1),也就是执行这两个分支后整个程序就会退出
        else if (strcmp(arrPassword, "0") == 0)
		{
			printf("---------------------------感谢你的使用-----------------------------\n");
			exit(0);
		}
		else                                                  //密码错误
		{
			int nChoose;
			printf("密码错误,你可以选择继续登录(输入1)或者退出该系统(输入其他任意数字)\n");
			scanf("%d", &nChoose);
			if (nChoose == 1)
				continue;
			else
				printf("---------------------------感谢你的使用-----------------------------\n");
			exit(0);
		}

至于这里为什么有两个退出的选择,是因为一个是直接退出,一个是在假如输入密码错误之后,给出选择,继续输入密码或者直接退出系统。用到exit(0)。


  1. 添加选手函数的实现
    说明:在这里有些变量在代码中没给出来的也不用担心,这里主要介绍的是实现的思路,有了思路,其实自己想写多少都可以。
				case 1:
				{
					printf("------------------------输入一个选手信息----------------------------\n");
					//人数++
					recordPl++;
					printf("该选手的名字是:\n");
					scanf("%s", arrPlname);
					printf("该选手的编号是:\n");
					scanf("%s", arrPlNum);
					printf("10位评委的打分(请一次输入10个分数)\n");
					for (i = 0; i < 10; i++)
					{
						scanf("%lf", &dbPlScore[i]);
					}
					//调用函数添加
					AddPlayerMSG(arrPlname, arrPlNum, dbPlScore);

					break;
				}
void AddPlayerMSG(char arrPlayername[10], char arrPlayNum[10], double* p_score)
//参数就是几个信息:分别为选手名字,选手编号,选手得分,这里得分传入地址是因为传入的是一个一维数组
{
	//传入信息进行添加
	if (arrPlayername == NULL && arrPlayNum == NULL || p_score == NULL)
	{
		//判断信息的合理性,避免给一个null的信息
		printf("选手信息输入错误.\n");
		return;
	}
	//开辟空间,准备插入到链表中
	struct PlayerNode* pTemp = malloc(sizeof(struct PlayerNode));
	if (pTemp != NULL)
	//表面申请空间成功
	{
		//传入信息到申请的节点中
		strcpy(pTemp->arrPlayername, arrPlayername);
		strcpy(pTemp->arrPlayerNumber, arrPlayNum);
		for (i = 0; i < 10; i++)
		{
			pTemp->dbstudent[i] = *(p_score + i);
		}
		pTemp->pNext = NULL;
		//作为第一个节点
		if (NULL == g_pEnd || NULL == g_pHead)
		{
			//新节点是头也是尾
			g_pEnd = pTemp;
			g_pHead = pTemp;
			return;
		}
		//作为新增其他节点
		else
		{
			//这里节点不是顺序添加,而是依据编号来添加,也就是说添加进链表的数据是已经排好序的
			struct PlayerNode* pT1, * pT2;
			//记住头节点,避免头节点在移动的过程中会丢失
			pT1 = g_pHead;
			pT2 = NULL;
			//当需要插入的节点的编号比头节点大
			while (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) > 0 && pT1->pNext != NULL)
			{
				//这时候就是一个遍历操作,一直遍历到比要插入的节点pTemp的编号大的那个节点,此时pT1指向的就是比编号pTemp大的那个节点
				pT2 = pT1;
				pT1 = pT1->pNext;
			}
			//如果找到了,就在pT1前面加
			if (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) <= 0)
			{
				//找到的是头节点
				if (g_pHead == pT1)
				{
					pTemp->pNext = g_pHead;
					g_pHead = pTemp;
					return;
				}
				//找到的节点不是头节点
				else
				{
					pT2->pNext = pTemp;
					pTemp->pNext = pT1;
					return;
				}
			}
			//找不到比插入节点pTemp编号更大的,此时pT1指向了尾节点,那么就让pT1来指向pTemp就好了
			else
			{
				pT1->pNext = pTemp;
				pTemp->pNext = NULL;
				g_pEnd = pTemp;
			}

		}
	}

}

在整个添加函数中,pT1是始终出现在其中的,原理很简单,遍历 — 找编号比要插入的节点要大的 — 插入节点。只是在这个过程中有一特殊情况要处理,比如pTemp比任何节点都要大或者比任何节点都要小。当然,如果pTemp刚好是在尾节点的话,别忘了pTem的下一个指向NULL,否则会出大问题。~~~真的。
在这里插入图片描述


  1. 删除选手信息并且把删除的选手的信息存入文档的实现
    说明:首先流程很简单:遍历链表找节点 — 存入文档 — 然后删除就可以啦
				case 3:
				{
					recordPl--;
					printf("-------------------------删除退赛选手的信息-----------------------------\n");
					//删除退赛的选手的信息 
					struct PlayerNode* pT = NULL;
					printf("请输入要删除的选手的编号:\n");
					scanf("%s", arrPlNum);
					//这个函数也不多介绍了,就是一个普通的遍历操作,返回找到的节点,找不到就返回null
					pT = FindPlayByNum(arrPlNum);
					//判断pT是否找到
					if (NULL != pT)       //找到了
					{
						//存数据进文档
						SavePlayToFileByOne(pT);
						//调用函数
						DeletePlayerMSG(pT);
					}
					break;
				}

重点是下面的两个函数

void SavePlayToFileByOne(struct PlayerNode*pT)
{
//下面的有一些变量也可以不用管,简单说明一下,RecordDelete是记录删除的人数
	RecordDelete++;
	int i = 0;
	struct PlayerNode* pTemp=pT;
	FILE* pFile = NULL;
	//写入文件,当删除的人数是1的时候
	if (RecordDelete == 1)
	{
		//用写的形式打开文件
		pFile = fopen("DeleteStudentDate.dat", "w");
		if (NULL == pFile)
		{
			printf("文件打开失败.\n");
			return;
		}
		//保存姓名编号:这里要注意格式,因为接下来的读取文件会用到
		fprintf(pFile, "%s : %s                ", pTemp->arrPlayerNumber, pTemp->arrPlayername);
		//保存选手得分
		fprintf(pFile, "%s", "得分: ");
		for (i = 0; i < 10; i++)
		{
			fprintf(pFile, "%.2f  ", pTemp->dbstudent[i]);
		}
		//注意:我这里是直到把信息全部写入文件才换行,也就是说姓名,编号,得分全是在一行的,至于为什么,下面会解释
		fprintf(pFile, "\n");

		//关闭文件
		fclose(pFile);
		printf("已将删除的选手的信息传入文档,如果需要恢复,请输入指令10\n");
	}
	//当删除的人数大于二,用接着写,防止原来的信息被擦除
	if (RecordDelete >= 2)
	{
		pFile = fopen("DeleteStudentDate.dat", "a+");
		if (NULL == pFile)
		{
			printf("文件打开失败.\n");
			return;
		}
		//保存姓名编号
		fprintf(pFile, "%s : %s                ", pTemp->arrPlayerNumber, pTemp->arrPlayername);
		//保存选手得分
		fprintf(pFile, "%s", "得分: ");
		for (i = 0; i < 10; i++)
		{
			fprintf(pFile, "%.2f  ", pTemp->dbstudent[i]);
		}
		fprintf(pFile, "\n");

		//关闭文件
		fclose(pFile);
		printf("已将删除的选手的信息传入文档,如果需要恢复,请输入指令10\n");

	}
		
}
//删除节点函数,这里假如能调用删除函数,那么就肯定是找到了节点,否则if条件进不来
void DeletePlayerMSG(struct PlayerNode* pNode)
{
	//只有一个节点,那找到的节点肯定是头节点啦
	if (g_pHead == g_pEnd)
	{
		free(g_pHead);
		g_pHead = NULL;
		g_pEnd = NULL;
		return;
	}
	//两个节点
	else if (g_pHead->pNext == g_pEnd)
	{
		if (g_pHead == pNode)           //找到的是头节点
		{
			free(g_pHead);
			g_pHead = g_pEnd;
			return;
		}
		else
		{
			free(g_pEnd);
			g_pEnd = g_pHead;
			g_pEnd->pNext = NULL;           //一定要赋值NULL
			return;
		}
	}
	//节点多于3个
	else
	{
		struct PlayerNode* pTemp = g_pHead;
		//头删除
		if (g_pHead == pNode)
		{
			pTemp = g_pHead;                 //记住头
			g_pHead = g_pHead->pNext;
			free(pTemp);
			pTemp = NULL;
			return;
		}
		//不是头
		while (pTemp != NULL)
		{
			//遍历找到要删除的节点的前一个,画个图就很好懂了,因为要对next操作
			if (pTemp->pNext == pNode)
			{
				//如果要删除的节点是尾节点
				if (pTemp->pNext == g_pEnd)
				{
					free(g_pEnd);
					g_pEnd = pTemp;
					g_pEnd->pNext = NULL;
					return;
				}
				//删除的不是尾节点
				else
				{
					//记住要删除的节点
					struct PlayerNode* pT = pTemp->pNext;
					if (pT != NULL)
					{
						pTemp->pNext = pT->pNext;
						free(pT);
						pT = NULL;
						return;
					}
				}
			}

			pTemp = pTemp->pNext;
		}
	}
}

这里第二个删除函数其实很好理解,不多说明,代码都有注释。第一个函数存入文件的函数。我分别是用了w 和 a+ 来写入文件,考虑到文件会被擦除的风险就这样处理了。但是我不清楚a+在第一次写入文件的时候会不会和w的处理一样,如果一样,那么这里的代码可以简化为都用a+的,少了几十行代码。然后关于输入格式的问题,我在上面是用了 fprintf 来输入,主要的原因也是为了控制输入的格式,等要读取文件的时候用 fscanf 就会比较轻松。这一点文件读取是一个问题,在下面的输入文件读取文件中会细说。


  1. 对节点进行排序
    说明:在这个函数中我用的方法是比较节点中的值,然后交换节点的值的方法,而不是交换两个节点。比如:对两个节点的编号交换,对分数交换等等。我觉得最重要的一个原因是对于节点的交换处理比较麻烦,需要遍历找出要交换的两个节点的前一个,然后再对next指针进行操作交换节点。这样来说,对于中间的两个节点的交换的处理很容易失误,尤其是要交换的节点有一个处于头或者尾的时候,交换节点的方法就比较难处理了。所以为了简单而且出错率低,我选择的是值交换而非节点交换。毕竟出错的话一行代码不知道要调试多久…唉,想起来都是泪。
				case 4:
				{
					printf("--------------------------排序----------------------------\n");
					//判断是否需要排序
					if (g_pHead == NULL || g_pHead == g_pEnd)
					{
						printf("数据过少,不需要排序,请添加选手信息.\n");
						break;
					}
					struct PlayerNode* pT1, * pT2;
					pT1 = g_pHead;
					//这里就相当于一个冒泡排序
					while (pT1 != NULL && pT1->pNext->pNext != NULL)
					{
						pT2 = pT1->pNext;
						while (pT2 != NULL)
						{
							if (Average(pT1) < Average(pT2))           //比较平均分
							{
								ExPlayerNode(pT1, pT2);                //交换节点
							}
							pT2 = pT2->pNext;
						}
						pT1 = pT1->pNext;
					}

					if (pT1->pNext->pNext == NULL)
					{
						pT2 = pT1->pNext;
						ExPlayerNode(pT1, pT2);
					}
					printf("已按成绩高低从大到小排序\n");
					break;
				}
void ExPlayerNode(struct PlayerNode* pNode1, struct PlayerNode* pNode2)
{
	char arrExchangeName[20] = { 0 };
	char arrExchangeNum[20] = { 0 };
	int i;
	double nTempscore;
	for (i = 0; i < 10; i++)                     //交换节点的分数
	{
		nTempscore = pNode1->dbstudent[i];
		pNode1->dbstudent[i] = pNode2->dbstudent[i];
		pNode2->dbstudent[i] = nTempscore;
	}
	//交换学号
	strcpy(arrExchangeNum, pNode1->arrPlayerNumber);
	strcpy(pNode1->arrPlayerNumber, pNode2->arrPlayerNumber);
	strcpy(pNode2->arrPlayerNumber, arrExchangeNum);
	//交换名字
	strcpy(arrExchangeName, pNode1->arrPlayername);
	strcpy(pNode1->arrPlayername, pNode2->arrPlayername);
	strcpy(pNode2->arrPlayername, arrExchangeName);
	return;
}


  1. 对文件操作
    终于到了最令人头疼的文件操作了,这里的8,9,10都是对文件的操作函数,这里涉及到的文件操作就是存入文件,读取文件,当然,作为第一个程序,功能还是要搞多点滴!所以我加了一个恢复删除的选手的文件,下面一个一个介绍。介绍统一写在源代码中,因为有流程比较好懂。
				case 8:
				{
					printf("---------------------------保存信息进文件----------------------------\n");
					SavePlayToFile();
					break;
				}

				case 9:
				{
					printf("---------------------------输出最初和最终的结果----------------------------\n");
					PrintOriAndFinal();
					break;
				}

				case 10:
				{
					char PointNum[20] = { 0 };
					printf("输入你需要找回的选手的姓名:\n");
					scanf("%s", PointNum);
					ReadFileToFindDelte(PointNum);
					break;
				}
void SavePlayToFile()
{
	//看标题就知道是写入文件啦,就是这名字起的有些不好
	//这里的存入文件有两个,一个是初始文件,一个是排名后的文件
	int a[1000] = { 0 };
	int i, j = 0;
	for (i = 0; i < recordPl; i++)
	{
		a[i] = i + 1;
	}
	//两个指针分别操作两个文件的存入
	struct PlayerNode* pTemp1 = g_pHead;
	struct PlayerNode* pTemp2 = g_pHead;
	int nFileOrder = 0;
	printf("---------写入文件---------\n");
	printf("~~1、初始存入\n~~2、排序后存入:\n~~3、取消该指令\n");
	printf("~~~请输入存储类型(1、2或3):\n");
	//输入指令,1就是初始存入,2就是排名后存入
	scanf("%d", &nFileOrder);
	FILE* pFile = NULL;
	FILE* pFile1 = NULL;
	//判断链表是否为NULL
	if (g_pHead == NULL)
	{
		printf("----没有选手信息----\n");
		return;
	}
	//写入文件
	switch (nFileOrder)
	{
	case 1:
	{
	//初始文件的意思就是你输入了信息,但没有排序,这就是初始文件
		pFile = fopen("playerOriginal.dat", "w");
		if (NULL == pFile)
		{
			printf("文件打开失败.\n");
			return;
		}
		while (pTemp2 != NULL)
		{
			//保存姓名编号,用fscanf,这里好处就体现出来了,如果是字节输入输出,那么就会产生问题,首先就是不能控制好格式,可以想想怎么控制得了想要的数据的字节,要知道每个人的名字不一样,分数也不一样,编号更不一样,有的长有的短。而用fscanf是为了控制格式,便于输出。
			fprintf(pFile, "%s : %s                ", pTemp2->arrPlayerNumber, pTemp2->arrPlayername);
			//保存选手得分
			fprintf(pFile, "%s", "得分: ");
			for (i = 0; i < 10; i++)
			{
				fprintf(pFile, "%.2f  ", pTemp2->dbstudent[i]);
			}
			fprintf(pFile, "\n");
			pTemp2 = pTemp2->pNext;
		}
		//关闭文件
		fclose(pFile);
		printf("----------已输入文件---------\n");
		break;
	}
	case 2:
	{

		pFile1 = fopen("playerGrade.dat", "w");
		if (NULL == pFile1)
		{
			printf("文件打开失败.\n");
			return;
		}
		while (pTemp2 != NULL)
		{
			//保存姓名编号
			fprintf(pFile1, "%s : %s                ", pTemp2->arrPlayerNumber, pTemp2->arrPlayername);
			//保存选手得分
			fprintf(pFile1, "%s  ", "得分:");
			for (i = 0; i < 10; i++)
			{
				fprintf(pFile1, "%.2f     ", pTemp2->dbstudent[i]);
			}
			fprintf(pFile1, "%s%d", "排名: ", a[j++]);
			fprintf(pFile1, "%s", "\n");
			pTemp2 = pTemp2->pNext;
			i++;
		}
		//关闭文件
		fclose(pFile1);
		printf("----------已输入文件---------\n");
		break;
	}
	case 3:
	{
		break;
	}
	}

}

这里就简单截图来看看这样的输入文件是什么格式
在这里插入图片描述
编号涉及个人信息,就不给出来了。这样看是不是就比较有顺序一点。


void PrintOriAndFinal()
{
	//打印最初的和最终的文件内容
	int j = 0, k = 0;                          //循环变量
	double SavePlayer[1000] = { 0 };      //存储数据
	FILE* pFile = NULL;               //输出原始文件
	FILE* pFile1 = NULL;              //输出排名后的文件
	pFile = fopen("playerOriginal.dat", "r");//当然是用r打开了
	pFile1 = fopen("playerGrade.dat", "r");
	//字符串用来存储读出的数据,估计估计自己一行有多少字符串,然后设大一点,就okok了
	char str1[200] = { 0 };
	char str2[200] = { 0 };
	printf("~~~~~~~~~~~~~~~~~~~~~排列后的数据~~~~~~~~~~~~~~~~~~~~~:\n");
	//这里因为不要求什么读取文件到链表中,所以直接一行一行读取就行了
	while (fgets(str1, 200, pFile1) != NULL)
	{
		k = 0;
		printf(str1);//输出读取的字符串
		//注意,这里清空字符串是为了防止上一次读出的数据有残留
		//设想一下,读出的字符串是不是有时候长度不一,那么不请空有什么影响?影响大了,首先输出就有问题,万一长度不一样,你想想,新读出的字符串能保证覆盖原有字符串吗?不可能吧,那么输出的时候就会有影响了。
		while (str1[k] != '\0')                     //清空
		{
			str1[k] = 0;
			k++;
		}
	}
	printf("~~~~~~~~~~~~~~~~~~~~~初始数据~~~~~~~~~~~~~~~~~~~~~:\n");
	while (fgets(str2, 200, pFile) != NULL)
	{
		k = 0;                                        //赋值为0
		printf("%s\n", str2);
		while (str2[k] != '\0')                     //清空
		{
			str2[k] = 0;
			k++;
		}
	}
}

这里再简单看下图片长啥样:
在这里插入图片描述
多了个排名:好看多了


//找回删除的选手信息
void ReadFileToFindDelte(char* PointNum)
{
	int Filerecord = 0;                        //记录观察是否找回
	int j = 0, k = 0;                          //循环变量
	FILE* pFile = NULL;
	pFile = fopen("DeleteStudentDate.dat", "r");
	if (pFile == NULL)
	{
		printf("打开文件失败!\n");
		return;
	}
	char c = 0;                            //装其他字符
	char d = 0;                            //装其他字符
	char str1[100] = { 0 };                //读出编号
	char str2[100] = { 0 };                //读出姓名
	char str3[100] = { 0 };                //读出其他字符
	char str4[100] = { 0 };                //读出其他字符
	char str5[100] = { 0 };                //读出其他字符
	double FileDate[10] = { 0 };           //读出文件数据
	//这里就有意思了,这也就是为什么前面用fprintf的原因,看!这格式不久有用了吗,看那个文件,四个%s分别读出四个字符串,第一个字符串就是我们要的编号。为什么?因为这个读出是遇到空格停下,所以看文件,格式是:字符串1 :字符串3  字符串4 得分....,字符串2去哪了?字符串2就是那个":",在这里面:也是一个字符串。
	//那么再来说说其他读出的函数,首先排除了fread,根据字节读出太难控制了。然后是fgets,也许你会说,我可以把姓名,编号,分数放在不同行。但是那样会不会太难看了,首先你的分数就要10行了,一个文件这么放数据,先不说代码有什么问题,首先规范整洁就是一个1大问题。然后就是代码读出的问题了,我也是试过这么干,但是每次读出字符串最后总会乱码,原因我也不清楚,可能是因为没清空字符串导致,但是读出了又要考虑清空,好麻烦的,而且万一你的名字和编号读在同一行,怎么分开也是个大问题,虽然算法可以搞定需求,但是写代码不就是为了简洁有效吗,那为什么不直接fscanf。
	while (fscanf(pFile, "%s%s%s%s%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", str1, str2, str3, str4, &FileDate[0], &FileDate[1], &FileDate[2], &FileDate[3], &FileDate[4], &FileDate[5], &FileDate[6], &FileDate[7], &FileDate[8], &FileDate[9]) != EOF)
	{
		//判断是不是需要的选手,你可能注意到了,这里str3装着的是名字,所以这是按名字恢复
		if (strcmp(str3, PointNum) == 0)
		{
			struct PlayerNode* pTemp = malloc(sizeof(struct PlayerNode));
			strcpy(pTemp->arrPlayerNumber, str1);
			strcpy(pTemp->arrPlayername, str3);
			for (j = 0; j < 10; j++)
			{
				pTemp->dbstudent[j] = FileDate[j];
			}
			pTemp->pNext = NULL;
			//这里就是第一个函数的操作了,加进链表,只是这里多了字符串清空
			//作为第一个节点
			if (NULL == g_pEnd || NULL == g_pHead)
			{
				g_pEnd = pTemp;
				g_pHead = pTemp;
				//清空,排除干扰
				for (k = 0; str1[k] != '\0'; k++)
				{
					str1[k] = 0;
				}
				for (k = 0; str3[k] != '\0'; k++)
				{
					str3[k] = 0;
				}
				for (k = 0; k < 10; k++)
				{
					FileDate[k] = 0;
				}
				Filerecord++;
				recordPl++;
				printf("已找回!\n");
				continue;
			}
			//作为新增其他节点
			else
			{
				struct PlayerNode* pT1, * pT2;
				pT1 = g_pHead;
				pT2 = NULL;
				//当需要插入的节点的编号比头节点大
				while (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) > 0 && pT1->pNext != NULL)
				{
					pT2 = pT1;
					pT1 = pT1->pNext;
				}
				//找到了,在pT1前面加
				if (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) <= 0)
				{
					//找到的是头节点
					if (g_pHead == pT1)
					{
						pTemp->pNext = g_pHead;
						g_pHead = pTemp;
						//清空,排除干扰
						for (k = 0; str1[k] != '\0'; k++)
						{
							str1[k] = 0;
						}
						for (k = 0; str3[k] != '\0'; k++)
						{
							str3[k] = 0;
						}
						for (k = 0; k < 10; k++)
						{
							FileDate[k] = 0;
						}
						Filerecord++;
						recordPl++;
						printf("已找回!\n");
						continue;
					}
					//找到的节点不是头节点
					else
					{
						pT2->pNext = pTemp;
						pTemp->pNext = pT1;
						//清空,排除干扰
						for (k = 0; str1[k] != '\0'; k++)
						{
							str1[k] = 0;
						}
						for (k = 0; str3[k] != '\0'; k++)
						{
							str3[k] = 0;
						}
						for (k = 0; k < 10; k++)
						{
							FileDate[k] = 0;
						}
						Filerecord++;
						recordPl++;
						printf("已找回!\n");
						continue;
					}
				}
				//找不到比插入节点编号更大的
				else
				{
					pT1->pNext = pTemp;
					pTemp->pNext = NULL;
					g_pEnd = pTemp;
					//清空,排除干扰
					for (k = 0; str1[k] != '\0'; k++)
					{
						str1[k] = 0;
					}
					for (k = 0; str3[k] != '\0'; k++)
					{
						str3[k] = 0;
					}
					for (k = 0; k < 10; k++)
					{
						FileDate[k] = 0;
					}
					Filerecord++;
					recordPl++;
					printf("已找回!\n");
					continue;
				}

			}
		}
		//不是就继续读
		else
		{
			continue;
		}

	}
	if (Filerecord == 0)
		printf("你需要恢复的选手不存在,请检查名字是否输入错误\n");
}

总体说一下吧,这里的文件操作特别需要注意格式,否则,代码100行,调试2小时不是梦!!!


7. 初始化链表(从文件读取数据形成链表)

简述:首先,你肯定不想要一个一个信息输入吧,那么提前把数据输入文件,然后一键读取,多方便

case 15:
				{
					int nOrderFile = 0;
					printf("-------------------------------------------从文件中读取选手信息构成链表-------------------------------------------------------\n");
					printf("***你是否需要从文件中读取信息到链表中然后继续操作(选择读取之后链表将会同步更新上一次存的初始数据)***\n");
					printf("¥¥¥1.选择继续\n¥¥¥2.退出\n请输入指令:");
					scanf("%d", &nOrderFile);
					if (nOrderFile == 1)
					{
						ReadFromFile();
						break;
					}
					else
					{
						break;
					}
				}
void ReadFromFile()
{
	int j = 0, k = 0;                          //循环变量
	FILE* pFile = NULL;
	pFile = fopen("playerOriginal.dat", "r");
	if (pFile == NULL)
	{
		printf("打开文件失败!\n");
		return;
	}
	char c = 0;                            //装其他字符
	char d = 0;                            //装其他字符
	char str1[100] = { 0 };                //读出编号
	char str2[100] = { 0 };                //读出姓名
	char str3[100] = { 0 };                //读出其他字符
	char str4[100] = { 0 };                //读出其他字符
	char str5[100] = { 0 };                //读出其他字符
	double FileDate[10] = { 0 };           //读出文件数据

	while (fscanf(pFile, "%s%s%s%s%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", str1, str2, str3, str4, &FileDate[0], &FileDate[1], &FileDate[2], &FileDate[3], &FileDate[4], &FileDate[5], &FileDate[6], &FileDate[7], &FileDate[8], &FileDate[9]) != EOF)
	{
		recordPl++;
		struct PlayerNode* pTemp = malloc(sizeof(struct PlayerNode));
		strcpy(pTemp->arrPlayerNumber, str1);
		strcpy(pTemp->arrPlayername, str3);
		for (j = 0; j < 10; j++)
		{
			pTemp->dbstudent[j] = FileDate[j];
		}
		pTemp->pNext = NULL;
		//作为第一个节点
		if (NULL == g_pEnd || NULL == g_pHead)
		{
			g_pEnd = pTemp;
			g_pHead = pTemp;
			//清空,排除干扰
			for (k = 0; str1[k] != '\0'; k++)
			{
				str1[k] = 0;
			}
			for (k = 0; str3[k] != '\0'; k++)
			{
				str3[k] = 0;
			}
			for (k = 0; k < 10; k++)
			{
				FileDate[k] = 0;
			}
			continue;
		}
		//作为新增其他节点
		else
		{
			struct PlayerNode* pT1, * pT2;
			pT1 = g_pHead;
			pT2 = NULL;
			//当需要插入的节点的编号比头节点大
			while (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) > 0 && pT1->pNext != NULL)
			{
				pT2 = pT1;
				pT1 = pT1->pNext;
			}
			//找到了,在pT1前面加
			if (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) <= 0)
			{
				//找到的是头节点
				if (g_pHead == pT1)
				{
					pTemp->pNext = g_pHead;
					g_pHead = pTemp;
					//清空,排除干扰
					for (k = 0; str1[k] != '\0'; k++)
					{
						str1[k] = 0;
					}
					for (k = 0; str3[k] != '\0'; k++)
					{
						str3[k] = 0;
					}
					for (k = 0; k < 10; k++)
					{
						FileDate[k] = 0;
					}
					continue;
				}
				//找到的节点不是头节点
				else
				{
					pT2->pNext = pTemp;
					pTemp->pNext = pT1;
					//清空,排除干扰
					for (k = 0; str1[k] != '\0'; k++)
					{
						str1[k] = 0;
					}
					for (k = 0; str3[k] != '\0'; k++)
					{
						str3[k] = 0;
					}
					for (k = 0; k < 10; k++)
					{
						FileDate[k] = 0;
					}
					continue;
				}
			}
			//找不到比插入节点编号更大的
			else
			{
				pT1->pNext = pTemp;
				pTemp->pNext = NULL;
				g_pEnd = pTemp;
				//清空,排除干扰
				for (k = 0; str1[k] != '\0'; k++)
				{
					str1[k] = 0;
				}
				for (k = 0; str3[k] != '\0'; k++)
				{
					str3[k] = 0;
				}
				for (k = 0; k < 10; k++)
				{
					FileDate[k] = 0;
				}
				continue;
			}

		}
	}
	printf("已从文件中读取信息!");
	return;

}

这不跟上面恢复删除的选手所用的读取方法是一样的?所以就不多说了


三、全部代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//清空链表 
void FreeLinkDate(void);
//输出管理员的界面
void Adminiprint(void);
//输出学生使用的界面
void Playerprint(void);
//输出所有的选手的信息(包含排名)
void ShowPlayerDateAddRank(void);
//添加选手信息
void AddPlayerMSG(char arrPlayername[10], char arrPlayNum[10], double* p_score);
//查看所有选手的数据
void ShowDate(void);
//查找指定编号的选手
struct PlayerNode* FindPlayByNum(char* PlayerNum);
//删除指定选手的信息
void DeletePlayerMSG(struct PlayerNode* pNode);
//修改指定选手的函数
void ChangePlayerDate(struct PlayerNode* pT);
//查看排名
struct PlayerNode* LookPointedPlayer_Rank(char* arrApointer);
//求节点平均值(去掉最高最低分)
double Average(struct PlayerNode* pTemp);
//交换节点数据
void ExPlayerNode(struct PlayerNode* pNode1, struct PlayerNode* pNode2);
//保存信息到文件
void SavePlayToFile();
//输出初始文件和最终文件
void PrintOriAndFinal();
//从文件中读取
void ReadFromFile();
//将删除的选手存入文件
void SavePlayToFileByOne(struct PlayerNode* pT);
//从文件中找到删除的选手并恢复
void ReadFileToFindDelte(char* PointNum);

struct PlayerNode
{
	char arrPlayername[20];
	char arrPlayerNumber[20];
	double dbstudent[10];
	struct PlayerNode* pNext;
};

//声明全局变量头和尾
struct PlayerNode* g_pHead;
struct PlayerNode* g_pEnd;

//循环控制符
int i;
//记录有多少位选手
int recordPl;
//记录有多少位删除的选手
int RecordDelete;


int main(void)
{

	printf("|----------------------------------------------------------|\n");
	printf("|                                                          |\n");
	printf("|                                                          |\n");
	printf("|                                                          |\n");
	printf("|        选手密码:********  管理员密码:123456789         |\n");
	printf("|***************    欢迎使用选手评分系统  *****************|\n");
	printf("|**** 请根据你的身份选择管理员登录或者选手输入密码登录 ****|\n");
	printf("|                                                          |\n");
	printf("|                                                          |\n");
	printf("|                                                          |\n");
	printf("|----------------------------------------------------------|\n");
	int nOrderAdmini = -1;                                    //管理员指令
	int nOrderPlay = -1;                                     //选手指令
	char arrPassword[20] = { 0 };                            //密码
	char arrPlname[20] = { 0 };                               //选手名字
	char arrPlNum[20] = { 0 };                                //编号
	double dbPlScore[10] = { 0 };                             //分数
	int nFlag = 1;                                           //退出循环的标示
	while (1)
	{
		printf("1、输入密码登陆.\n2、输入0退出系统.\n请根据需要输入密码或指令0:\n");
		printf("|-----------------------------------------------------------|\n");
		printf("|------选手密码:********\t管理员密码:123456789-------|\n");
		printf("|-----------------------------------------------------------|\n");
		scanf("%s", arrPassword);                            //输入密码

		if (strcmp(arrPassword, "123456789") == 0)           //判断是否是管理员系统
		{
			printf("你已经登录管理员系统\n");
			Adminiprint();
			printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
			printf("~~~~~~PS:如果需要从文件中读取初始学生,请选择指令15,然后继续操作(建议一开始选择)~~~~~~\n");
			printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
			nFlag = 1;
			while (nFlag)
			{
				printf("请输入指令(请不要输入字符,输入11查看指令):");
				scanf("%d", &nOrderAdmini);
				switch (nOrderAdmini)
				{
				case 1:
				{
					printf("------------------------输入一个选手信息----------------------------\n");
					//人数++
					recordPl++;
					printf("该选手的名字是:\n");
					scanf("%s", arrPlname);
					printf("该选手的编号是:\n");
					scanf("%s", arrPlNum);
					printf("10位评委的打分(请一次输入10个分数)\n");
					for (i = 0; i < 10; i++)
					{
						scanf("%lf", &dbPlScore[i]);
					}
					//调用函数添加
					AddPlayerMSG(arrPlname, arrPlNum, dbPlScore);

					break;
				}

				case 2:
				{
					printf("-----------------------查找指定选手信息---------------------------\n");
					//查找指定编号的选手的信息
					struct PlayerNode* pT = NULL;
					printf("请输入要查找的选手的编号:\n");
					scanf("%s", arrPlNum);
					pT = FindPlayByNum(arrPlNum);
					if (pT != NULL)
					{
						printf("已找到");
						printf("\n");
						printf("该选手的编号和名字是:\n");
						printf("%s:%s\n", pT->arrPlayerNumber, pT->arrPlayername);
						printf("该选手的得分情况是:\n");
						printf("分数1    分数2   分数3    分数4    分数5    分数6    份数7    分数8    分数9    分数10\n");
						for (i = 0; i < 10; i++)
						{
							printf("%.2f    ", pT->dbstudent[i]);
						}
						printf("\n");

					}
					break;
				}

				case 3:
				{
					recordPl--;
					printf("-------------------------删除退赛选手的信息-----------------------------\n");
					//删除退赛的选手的信息 
					struct PlayerNode* pT = NULL;
					printf("请输入要删除的选手的编号:\n");
					scanf("%s", arrPlNum);
					pT = FindPlayByNum(arrPlNum);
					//判断pT是否找到
					if (NULL != pT)       //找到了
					{
						//存数据进文档
						SavePlayToFileByOne(pT);
						//调用函数
						DeletePlayerMSG(pT);
					}
					break;
				}

				case 4:
				{
					printf("--------------------------排序----------------------------\n");
					//判断是否需要排序
					if (g_pHead == NULL || g_pHead == g_pEnd)
					{
						printf("数据过少,不需要排序,请添加选手信息.\n");
						break;
					}
					struct PlayerNode* pT1, * pT2;
					pT1 = g_pHead;
					while (pT1 != NULL && pT1->pNext->pNext != NULL)
					{
						pT2 = pT1->pNext;
						while (pT2 != NULL)
						{
							if (Average(pT1) < Average(pT2))           //比较平均分
							{
								ExPlayerNode(pT1, pT2);                //交换节点
							}
							pT2 = pT2->pNext;
						}
						pT1 = pT1->pNext;
					}

					if (pT1->pNext->pNext == NULL)
					{
						pT2 = pT1->pNext;
						ExPlayerNode(pT1, pT2);
					}
					printf("已按成绩高低从大到小排序\n");
					break;
				}

				case 5:
				{
					printf("------------------------查看排序----------------------------\n");
					printf("~~~建议按分数从高到低排序后查看,你是否确定你已经排好序了?\n");
					printf("如果选择继续,请输入1,否则输入其他任意数字退出\n");
					int recordPoint;
					scanf("%d", &recordPoint);
					if (recordPoint == 1)
					{
						ShowPlayerDateAddRank();
						break;
					}
					else
					{
						break;
					}
				}

				case 6:
				{
					printf("---------------------------查看所有选手的数据---------------------------\n");
					//查看所有选手的数据
					ShowDate();
					break;
				}

				case 7:
				{
					printf("------------------------查看指定选手的排名--------------------------\n");
					int nRank = 0;                      //排名
					struct PlayerNode* pTemp;
					pTemp = g_pHead;
					printf("请输入你想查找的选手的编号:\n");
					char arrApointerNum[20];            //需要查找的选手
					scanf("%s", arrApointerNum);
					struct PlayerNode* pFindByNum = LookPointedPlayer_Rank(arrApointerNum);
					if (pFindByNum == NULL)
						break;
					else
					{
						while (pTemp != NULL)
						{
							//排名加一
							nRank++;
							//循环查找排名
							if (pTemp == pFindByNum)
							{
								printf("该选手的排名是:%d\n", nRank);
								nRank = 0;
								break;
							}
							pTemp = pTemp->pNext;
						}
						nRank = 0;
						break;

					}

				}

				case 8:
				{
					printf("---------------------------保存信息进文件----------------------------\n");
					SavePlayToFile();
					break;
				}

				case 9:
				{
					printf("---------------------------输出最初和最终的结果----------------------------\n");
					PrintOriAndFinal();
					break;
				}

				case 10:
				{
					char PointNum[20] = { 0 };
					printf("输入你需要找回的选手的姓名:\n");
					scanf("%s", PointNum);
					ReadFileToFindDelte(PointNum);
					break;
				}

				case 11:
				{
					printf("---------------------------显示指令界面---------------------------\n");
					Adminiprint();
					break;
				}

				case 12:
				{
					printf("-----------------------------退出系统------------------------------\n");
					printf("---------------------------感谢你的使用-----------------------------\n");
					exit(0);
					break;
				}

				case 13:
				{
					recordPl = 0;
					printf("-----------------------------删除所有选手的信息-----------------------------\n");
					FreeLinkDate();
					printf("~~~~已完全删除~~~~\n");
					break;
				}

				case 14:
				{
					char arrChangeNum[20] = { 0 };
					printf("-----------------------------修改指定选手的数据(根据编号)-----------------------------\n");
					printf("输入需要修改的选手的编号:");
					scanf("%s", arrChangeNum);
					struct PlayerNode* ChangeDate = FindPlayByNum(arrChangeNum);
					if (ChangeDate == NULL)
					{
						break;
					}
					else
					{
						printf("找到了!\n");
						ChangePlayerDate(ChangeDate);
						break;
					}

				}

				case 15:
				{
					int nOrderFile = 0;
					printf("-------------------------------------------从文件中读取选手信息构成链表-------------------------------------------------------\n");
					printf("***你是否需要从文件中读取信息到链表中然后继续操作(选择读取之后链表将会同步更新上一次存的初始数据)***\n");
					printf("¥¥¥1.选择继续\n¥¥¥2.退出\n请输入指令:");
					scanf("%d", &nOrderFile);
					if (nOrderFile == 1)
					{
						ReadFromFile();
						break;
					}
					else
					{
						break;
					}
				}

				case 16:
				{
					printf("当前链表中的选手的个数:%d\n", recordPl);
					break;
				}

				case 0:
				{
					printf("------------------------------退出登录------------------------------\n");
					printf("你已经退出登录,若要继续登录操作,请输入密码,否则输入0退出系统!\n");
					//标识符赋值0
					nFlag = 0;
					break;
				}

				default:
				{
					printf("你输入的参数指令不合法\n");
					break;
				}
				}
			}

		}

		else if (strcmp(arrPassword, "********") == 0)      //判断是否是选手系统
		{
			int  nPlayerFlag = 1;
			printf("你已经登录选手系统\n\n");
			Playerprint();
			while (nPlayerFlag)
			{
				printf("请输入指令(请不要输入字符):");
				scanf("%d", &nOrderPlay);
				switch (nOrderPlay)
				{
				case 1:
				{
					//查找指定编号的选手的信息
					struct PlayerNode* pT = NULL;
					printf("请输入你的编号:\n");
					scanf("%s", arrPlNum);
					pT = FindPlayByNum(arrPlNum);
					if (pT != NULL)
					{
						printf("已找到");
						printf("\n");
						printf("该选手的编号和名字是:\n");
						printf("%s:%s\n", pT->arrPlayerNumber, pT->arrPlayername);
						printf("该选手的得分情况是:\n");
						printf("分数1    分数2   分数3    分数4    分数5    分数6    分数7    分数8    分数9    分数10\n");
						for (i = 0; i < 10; i++)
						{
							printf("%.2f    ", pT->dbstudent[i]);
						}
						printf("\n");

					}
					break;
				}
				case 2:
				{
					int nRank = 0;                      //排名
					struct PlayerNode* pTemp;
					pTemp = g_pHead;
					printf("请输入你的编号:\n");
					char arrApointerNum[20];            //需要查找的选手
					scanf("%s", arrApointerNum);
					struct PlayerNode* pFindByNum = LookPointedPlayer_Rank(arrApointerNum);
					if (pFindByNum == NULL)
						break;
					else
					{
						while (pTemp != NULL)
						{
							//排名加一
							nRank++;
							//循环查找排名
							if (pTemp == pFindByNum)
							{
								printf("该选手的排名是:%d\n", nRank);
								nRank = 0;
								break;
							}
							pTemp = pTemp->pNext;
						}
						nRank = 0;
						break;

					}
				}

				case 3:
				{
					printf("你已经退出登录,若要继续登录操作,请输入密码,否则输入0退出系统,谢谢配合!\n");
					//标识符赋值0
					nPlayerFlag = 0;
					break;
				}

				case 4:
				{
					exit(0);
					break;
				}
				default:
				{
					printf("输入指令有误!请重新输入.");
					break;
				}
				}
			}

		}

		else if (strcmp(arrPassword, "0") == 0)
		{
			printf("---------------------------感谢你的使用-----------------------------\n");
			exit(0);
		}
		else                                                  //密码错误
		{
			int nChoose;
			printf("密码错误,你可以选择继续登录(输入1)或者退出该系统(输入其他任意数字)\n");
			scanf("%d", &nChoose);
			if (nChoose == 1)
				continue;
			else
				printf("---------------------------感谢你的使用-----------------------------\n");
			exit(0);
		}



	}
	FreeLinkDate();             //释放链表
	system("pause>0");
	return 0;
}

void FreeLinkDate(void)
{
	if (NULL == g_pHead)
	{
		printf("链表为空,无需清空\n");
	}
	struct PlayerNode* pTemp = g_pHead;        //记住头
	while (g_pHead != NULL)
	{
		pTemp = g_pHead;
		g_pHead = g_pHead->pNext;              //头指向下一个
		free(pTemp);                           //释放节点
	}
}

void Playerprint(void)
{
	printf("------------------------------------------------------------------\n");
	printf("------------------------------------------------------------------\n");
	printf("**********************  比赛评分系统(学生专用) *********************\n");
	printf("*********************** 本系统操作指令如下 *************************\n");
	printf("***                       1.查看成绩                            ***\n");
	printf("***                       2.查看排名                            ***\n");
	printf("***                       3.退出登陆                            ***\n");
	printf("                          4.退出系统                            ***\n");
	printf("******************************************************************\n");
	printf("------------------------------------------------------------------\n");
	printf("------------------------------------------------------------------\n");

}

void Adminiprint(void)
{
	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("***                  9.输出初始和最终的信息                               ***\n ");
	printf("***                  10.恢复指定删除的选手的信息                          ***\n ");
	printf("***                  11.查看指令                                          ***\n ");
	printf("***                  12.退出系统                                          ***\n ");
	printf("***                  13.删除所有选手的信息                                ***\n ");
	printf("***                  14.修改指定选手的信息                                ***\n ");
	printf("***                  15.从文件中读取信息进链表                            ***\n ");
	printf("***                  16.查看当前链表中有多少个选手                        ***\n ");
	printf("***                  0.退出登陆                                           ***\n ");
	printf("*****************************************************************************\n");
}


void AddPlayerMSG(char arrPlayername[10], char arrPlayNum[10], double* p_score)
{
	if (arrPlayername == NULL && arrPlayNum == NULL || p_score == NULL)
	{
		printf("选手信息输入错误.\n");
		return;
	}
	struct PlayerNode* pTemp = malloc(sizeof(struct PlayerNode));
	if (pTemp != NULL)
	{
		strcpy(pTemp->arrPlayername, arrPlayername);
		strcpy(pTemp->arrPlayerNumber, arrPlayNum);
		for (i = 0; i < 10; i++)
		{
			pTemp->dbstudent[i] = *(p_score + i);
		}
		pTemp->pNext = NULL;
		//作为第一个节点
		if (NULL == g_pEnd || NULL == g_pHead)
		{
			g_pEnd = pTemp;
			g_pHead = pTemp;
			return;
		}
		//作为新增其他节点
		else
		{
			struct PlayerNode* pT1, * pT2;
			pT1 = g_pHead;
			pT2 = NULL;
			//当需要插入的节点的编号比头节点大
			while (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) > 0 && pT1->pNext != NULL)
			{
				pT2 = pT1;
				pT1 = pT1->pNext;
			}
			//找到了,在pT1前面加
			if (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) <= 0)
			{
				//找到的是头节点
				if (g_pHead == pT1)
				{
					pTemp->pNext = g_pHead;
					g_pHead = pTemp;
					return;
				}
				//找到的节点不是头节点
				else
				{
					pT2->pNext = pTemp;
					pTemp->pNext = pT1;
					return;
				}
			}
			//找不到比插入节点编号更大的
			else
			{
				pT1->pNext = pTemp;
				pTemp->pNext = NULL;
				g_pEnd = pTemp;
			}

		}
	}

}

void ShowDate(void)
{
	struct PlayerNode* pTemp = g_pHead;        //防止头改变
	if (g_pHead == NULL)
	{
		printf("该链表无数据!");
	}
	while (pTemp != NULL)
	{
		printf("\n");
		printf("该选手的编号和名字是:\n");
		printf("%s:%s\n", pTemp->arrPlayerNumber, pTemp->arrPlayername);
		printf("该选手的得分情况是:\n");
		printf("分数1    分数2   分数3    分数4    分数5    分数6    分数7    分数8    分数9    分数10\n");
		for (i = 0; i < 10; i++)
		{
			printf("%.2f    ", pTemp->dbstudent[i]);
		}
		printf("\n该选手的平均分是:%.2f", Average(pTemp));
		printf("\n");
		pTemp = pTemp->pNext;
	}
	printf("\n");
	return;

}

struct PlayerNode* FindPlayByNum(char* PlayerNum)
{
	struct PlayerNode* pTemp = g_pHead;
	//检查参数合法性
	if (PlayerNum == NULL)
	{
		printf("选手编号输入错误,请检查编号输入\n");
		return NULL;
	}
	if (NULL == g_pHead || NULL == g_pEnd)
	{
		printf("链表为空,请检查");
		return NULL;
	}
	while (pTemp != NULL)
	{
		//比较是否是需要的节点 
		if (0 == strcmp(pTemp->arrPlayerNumber, PlayerNum))
		{
			return pTemp;
		}
		pTemp = pTemp->pNext;
	}
	//没找到
	printf("没找到选手的编号,请检查是否输入错误.\n");
	return NULL;
}

void DeletePlayerMSG(struct PlayerNode* pNode)
{
	//只有一个节点
	if (g_pHead == g_pEnd)
	{
		free(g_pHead);
		g_pHead = NULL;
		g_pEnd = NULL;
		return;
	}
	//两个节点
	else if (g_pHead->pNext == g_pEnd)
	{
		if (g_pHead == pNode)           //找到的是头节点
		{
			free(g_pHead);
			g_pHead = g_pEnd;
			return;
		}
		else
		{
			free(g_pEnd);
			g_pEnd = g_pHead;
			g_pEnd->pNext = NULL;           //一定要赋值NULL
			return;
		}
	}
	//节点多于3个
	else
	{
		struct PlayerNode* pTemp = g_pHead;
		//头删除
		if (g_pHead == pNode)
		{
			pTemp = g_pHead;                 //记住头
			g_pHead = g_pHead->pNext;
			free(pTemp);
			pTemp = NULL;
			return;
		}
		//不是头
		while (pTemp != NULL)
		{
			//找到要删除的节点的前一个
			if (pTemp->pNext == pNode)
			{
				//如果要删除的节点是尾节点
				if (pTemp->pNext == g_pEnd)
				{
					free(g_pEnd);
					g_pEnd = pTemp;
					g_pEnd->pNext = NULL;
					return;
				}
				//删除的不是尾节点
				else
				{
					//记住要删除的节点
					struct PlayerNode* pT = pTemp->pNext;
					if (pT != NULL)
					{
						pTemp->pNext = pT->pNext;
						free(pT);
						pT = NULL;
						return;
					}
				}
			}

			pTemp = pTemp->pNext;
		}
	}
}

void ChangePlayerDate(struct PlayerNode* pT)
{
	int j = 0;                                 //循环变量
	int ChangeScoreNum;                        //要修改第几个得分
	int ChangeOrder;                           //操作指令
	int ChangeOrderScore;                      //修改分数的指令
	char ChangeName[20] = { 0 };
	printf("请输入你要修改的数据:\n");
	printf("~~~1.修改姓名\n~~~2.修改得分\n~~~3.退出修改\n");
	scanf("%d", &ChangeOrder);
	switch (ChangeOrder)
	{
	case 1:
	{
		printf("请输入修改后的姓名:\n");
		scanf("%s", ChangeName);
		strcpy(pT->arrPlayername, ChangeName);
		printf("~~~已修改完毕~~~");
		break;
	}
	case 2:
	{
		printf("|----------------------------------------------------------------------------------------------------------------------------------------------------|\n");
		printf("|该选手的编号和名字是:                                                                                                                              |\n");
		printf(" %s:%s                                                                                                                                       \n", pT->arrPlayerNumber, pT->arrPlayername);
		printf(" 该选手的得分情况是:\n");
		for (j = 0; j < 10; j++)
		{
			printf(" Score%d:", j + 1);
			printf("%.2f ", pT->dbstudent[j]);
		}
		printf("\n");
		printf("|----------------------------------------------------------------------------------------------------------------------------------------------------|");
		printf("\n");
		printf("请输入你要修改一个还是全部得分\n");
		printf("~~~10.全部\n");
		printf("~~~01.一个\n");
		scanf("%d", &ChangeOrderScore);
		if (ChangeOrderScore == 10)
		{
			printf("请重新输入得分:\n");
			for (j = 0; j < 10; j++)
			{
				printf("Score %d:", j + 1);
				scanf("%lf", &pT->dbstudent[j]);
				printf("~~~~~~~已修改~~~~~~~\n");
			}
		}
		if (ChangeOrderScore == 01)
		{
			printf("请输入你要修改第几个得分(1-10):");
			scanf("%d", &ChangeScoreNum);
			printf("请输入修改后的得分:");
			scanf("%lf", &pT->dbstudent[ChangeScoreNum - 1]);
			printf("~~~~~~~已修改~~~~~~~\n");
		}
		break;
	}
	case 3:
	{
		break;
	}

	}
	return;
}

void ShowPlayerDateAddRank(void)
{
	int j = 1;
	struct PlayerNode* pTemp = g_pHead;        //防止头改变
	if (g_pHead == NULL)
	{
		printf("该链表无数据!");
	}
	while (pTemp != NULL)
	{
		printf("\n");
		printf("该选手的编号和名字是:\n");
		printf("%s:%s\n", pTemp->arrPlayerNumber, pTemp->arrPlayername);
		if (j <= recordPl)
		{
			printf("排名:%d\n", j++);
		}
		printf("该选手的得分情况是:\n");
		printf("分数1    分数2   分数3    分数4    分数5    分数6    分数7    分数8    分数9    分数10\n");
		for (i = 0; i < 10; i++)
		{
			printf("%.2f    ", pTemp->dbstudent[i]);
		}
		printf("\n该选手的平均分是:%.2f", Average(pTemp));
		printf("\n");
		pTemp = pTemp->pNext;
	}
	printf("\n");
	return;
}

void SavePlayToFile()
{
	int a[1000] = { 0 };
	int i, j = 0;
	for (i = 0; i < recordPl; i++)
	{
		a[i] = i + 1;
	}
	struct PlayerNode* pTemp1 = g_pHead;
	struct PlayerNode* pTemp2 = g_pHead;
	int nFileOrder = 0;
	printf("---------写入文件---------\n");
	printf("~~1、初始存入\n~~2、排序后存入:\n~~3、取消该指令\n");
	printf("~~~请输入存储类型(1、2或3):\n");
	scanf("%d", &nFileOrder);
	FILE* pFile = NULL;
	FILE* pFile1 = NULL;
	//判断链表是否为NULL
	if (g_pHead == NULL)
	{
		printf("----没有选手信息----\n");
		return;
	}
	//写入文件
	switch (nFileOrder)
	{
	case 1:
	{
		pFile = fopen("playerOriginal.dat", "w");
		if (NULL == pFile)
		{
			printf("文件打开失败.\n");
			return;
		}
		while (pTemp2 != NULL)
		{
			//保存姓名编号
			fprintf(pFile, "%s : %s                ", pTemp2->arrPlayerNumber, pTemp2->arrPlayername);
			//保存选手得分
			fprintf(pFile, "%s", "得分: ");
			for (i = 0; i < 10; i++)
			{
				fprintf(pFile, "%.2f  ", pTemp2->dbstudent[i]);
			}
			fprintf(pFile, "\n");
			pTemp2 = pTemp2->pNext;
		}
		//关闭文件
		fclose(pFile);
		printf("----------已输入文件---------\n");
		break;
	}
	case 2:
	{

		pFile1 = fopen("playerGrade.dat", "w");
		if (NULL == pFile1)
		{
			printf("文件打开失败.\n");
			return;
		}
		while (pTemp2 != NULL)
		{
			//保存姓名编号
			fprintf(pFile1, "%s : %s                ", pTemp2->arrPlayerNumber, pTemp2->arrPlayername);
			//保存选手得分
			fprintf(pFile1, "%s  ", "得分:");
			for (i = 0; i < 10; i++)
			{
				fprintf(pFile1, "%.2f     ", pTemp2->dbstudent[i]);
			}
			fprintf(pFile1, "%s%d", "排名: ", a[j++]);
			fprintf(pFile1, "%s", "\n");
			pTemp2 = pTemp2->pNext;
			i++;
		}
		//关闭文件
		fclose(pFile1);
		printf("----------已输入文件---------\n");
		break;
	}
	case 3:
	{
		break;
	}
	}

}

void PrintOriAndFinal()
{

	int j = 0, k = 0;                          //循环变量
	double SavePlayer[1000] = { 0 };      //存储数据
	FILE* pFile = NULL;               //输出原始文件
	FILE* pFile1 = NULL;              //输出排名后的文件
	pFile = fopen("playerOriginal.dat", "r");
	pFile1 = fopen("playerGrade.dat", "r");
	char str1[200] = { 0 };
	char str2[200] = { 0 };
	printf("~~~~~~~~~~~~~~~~~~~~~排列后的数据~~~~~~~~~~~~~~~~~~~~~:\n");
	while (fgets(str1, 200, pFile1) != NULL)
	{
		k = 0;
		printf(str1);
		while (str1[k] != '\0')                     //清空
		{
			str1[k] = 0;
			k++;
		}
	}
	printf("~~~~~~~~~~~~~~~~~~~~~初始数据~~~~~~~~~~~~~~~~~~~~~:\n");
	while (fgets(str2, 200, pFile) != NULL)
	{
		k = 0;                                        //赋值为0
		printf("%s\n", str2);
		while (str2[k] != '\0')                     //清空
		{
			str2[k] = 0;
			k++;
		}
	}
}

void ReadFromFile()
{
	int j = 0, k = 0;                          //循环变量
	FILE* pFile = NULL;
	pFile = fopen("playerOriginal.dat", "r");
	if (pFile == NULL)
	{
		printf("打开文件失败!\n");
		return;
	}
	char c = 0;                            //装其他字符
	char d = 0;                            //装其他字符
	char str1[100] = { 0 };                //读出编号
	char str2[100] = { 0 };                //读出姓名
	char str3[100] = { 0 };                //读出其他字符
	char str4[100] = { 0 };                //读出其他字符
	char str5[100] = { 0 };                //读出其他字符
	double FileDate[10] = { 0 };           //读出文件数据

	while (fscanf(pFile, "%s%s%s%s%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", str1, str2, str3, str4, &FileDate[0], &FileDate[1], &FileDate[2], &FileDate[3], &FileDate[4], &FileDate[5], &FileDate[6], &FileDate[7], &FileDate[8], &FileDate[9]) != EOF)
	{
		recordPl++;
		struct PlayerNode* pTemp = malloc(sizeof(struct PlayerNode));
		strcpy(pTemp->arrPlayerNumber, str1);
		strcpy(pTemp->arrPlayername, str3);
		for (j = 0; j < 10; j++)
		{
			pTemp->dbstudent[j] = FileDate[j];
		}
		pTemp->pNext = NULL;
		//作为第一个节点
		if (NULL == g_pEnd || NULL == g_pHead)
		{
			g_pEnd = pTemp;
			g_pHead = pTemp;
			//清空,排除干扰
			for (k = 0; str1[k] != '\0'; k++)
			{
				str1[k] = 0;
			}
			for (k = 0; str3[k] != '\0'; k++)
			{
				str3[k] = 0;
			}
			for (k = 0; k < 10; k++)
			{
				FileDate[k] = 0;
			}
			continue;
		}
		//作为新增其他节点
		else
		{
			struct PlayerNode* pT1, * pT2;
			pT1 = g_pHead;
			pT2 = NULL;
			//当需要插入的节点的编号比头节点大
			while (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) > 0 && pT1->pNext != NULL)
			{
				pT2 = pT1;
				pT1 = pT1->pNext;
			}
			//找到了,在pT1前面加
			if (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) <= 0)
			{
				//找到的是头节点
				if (g_pHead == pT1)
				{
					pTemp->pNext = g_pHead;
					g_pHead = pTemp;
					//清空,排除干扰
					for (k = 0; str1[k] != '\0'; k++)
					{
						str1[k] = 0;
					}
					for (k = 0; str3[k] != '\0'; k++)
					{
						str3[k] = 0;
					}
					for (k = 0; k < 10; k++)
					{
						FileDate[k] = 0;
					}
					continue;
				}
				//找到的节点不是头节点
				else
				{
					pT2->pNext = pTemp;
					pTemp->pNext = pT1;
					//清空,排除干扰
					for (k = 0; str1[k] != '\0'; k++)
					{
						str1[k] = 0;
					}
					for (k = 0; str3[k] != '\0'; k++)
					{
						str3[k] = 0;
					}
					for (k = 0; k < 10; k++)
					{
						FileDate[k] = 0;
					}
					continue;
				}
			}
			//找不到比插入节点编号更大的
			else
			{
				pT1->pNext = pTemp;
				pTemp->pNext = NULL;
				g_pEnd = pTemp;
				//清空,排除干扰
				for (k = 0; str1[k] != '\0'; k++)
				{
					str1[k] = 0;
				}
				for (k = 0; str3[k] != '\0'; k++)
				{
					str3[k] = 0;
				}
				for (k = 0; k < 10; k++)
				{
					FileDate[k] = 0;
				}
				continue;
			}

		}
	}
	printf("已从文件中读取信息!");
	return;

}

void SavePlayToFileByOne(struct PlayerNode* pT)
{
	RecordDelete++;
	int i = 0;
	struct PlayerNode* pTemp = pT;
	FILE* pFile = NULL;
	//写入文件
	if (RecordDelete == 1)
	{
		pFile = fopen("DeleteStudentDate.dat", "w");
		if (NULL == pFile)
		{
			printf("文件打开失败.\n");
			return;
		}
		//保存姓名编号
		fprintf(pFile, "%s : %s                ", pTemp->arrPlayerNumber, pTemp->arrPlayername);
		//保存选手得分
		fprintf(pFile, "%s", "得分: ");
		for (i = 0; i < 10; i++)
		{
			fprintf(pFile, "%.2f  ", pTemp->dbstudent[i]);
		}
		fprintf(pFile, "\n");

		//关闭文件
		fclose(pFile);
		printf("已将删除的选手的信息传入文档,如果需要恢复,请输入指令10\n");
	}
	if (RecordDelete >= 2)
	{
		pFile = fopen("DeleteStudentDate.dat", "a+");
		if (NULL == pFile)
		{
			printf("文件打开失败.\n");
			return;
		}
		//保存姓名编号
		fprintf(pFile, "%s : %s                ", pTemp->arrPlayerNumber, pTemp->arrPlayername);
		//保存选手得分
		fprintf(pFile, "%s", "得分: ");
		for (i = 0; i < 10; i++)
		{
			fprintf(pFile, "%.2f  ", pTemp->dbstudent[i]);
		}
		fprintf(pFile, "\n");

		//关闭文件
		fclose(pFile);
		printf("已将删除的选手的信息传入文档,如果需要恢复,请输入指令10\n");

	}

}

void ReadFileToFindDelte(char* PointNum)
{
	int Filerecord = 0;                        //记录观察是否找回
	int j = 0, k = 0;                          //循环变量
	FILE* pFile = NULL;
	pFile = fopen("DeleteStudentDate.dat", "r");
	if (pFile == NULL)
	{
		printf("打开文件失败!\n");
		return;
	}
	char c = 0;                            //装其他字符
	char d = 0;                            //装其他字符
	char str1[100] = { 0 };                //读出编号
	char str2[100] = { 0 };                //读出姓名
	char str3[100] = { 0 };                //读出其他字符
	char str4[100] = { 0 };                //读出其他字符
	char str5[100] = { 0 };                //读出其他字符
	double FileDate[10] = { 0 };           //读出文件数据

	while (fscanf(pFile, "%s%s%s%s%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", str1, str2, str3, str4, &FileDate[0], &FileDate[1], &FileDate[2], &FileDate[3], &FileDate[4], &FileDate[5], &FileDate[6], &FileDate[7], &FileDate[8], &FileDate[9]) != EOF)
	{
		//判断是不是需要的选手
		if (strcmp(str3, PointNum) == 0)
		{
			struct PlayerNode* pTemp = malloc(sizeof(struct PlayerNode));
			strcpy(pTemp->arrPlayerNumber, str1);
			strcpy(pTemp->arrPlayername, str3);
			for (j = 0; j < 10; j++)
			{
				pTemp->dbstudent[j] = FileDate[j];
			}
			pTemp->pNext = NULL;
			//作为第一个节点
			if (NULL == g_pEnd || NULL == g_pHead)
			{
				g_pEnd = pTemp;
				g_pHead = pTemp;
				//清空,排除干扰
				for (k = 0; str1[k] != '\0'; k++)
				{
					str1[k] = 0;
				}
				for (k = 0; str3[k] != '\0'; k++)
				{
					str3[k] = 0;
				}
				for (k = 0; k < 10; k++)
				{
					FileDate[k] = 0;
				}
				Filerecord++;
				recordPl++;
				printf("已找回!\n");
				continue;
			}
			//作为新增其他节点
			else
			{
				struct PlayerNode* pT1, * pT2;
				pT1 = g_pHead;
				pT2 = NULL;
				//当需要插入的节点的编号比头节点大
				while (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) > 0 && pT1->pNext != NULL)
				{
					pT2 = pT1;
					pT1 = pT1->pNext;
				}
				//找到了,在pT1前面加
				if (strcmp(pTemp->arrPlayerNumber, pT1->arrPlayerNumber) <= 0)
				{
					//找到的是头节点
					if (g_pHead == pT1)
					{
						pTemp->pNext = g_pHead;
						g_pHead = pTemp;
						//清空,排除干扰
						for (k = 0; str1[k] != '\0'; k++)
						{
							str1[k] = 0;
						}
						for (k = 0; str3[k] != '\0'; k++)
						{
							str3[k] = 0;
						}
						for (k = 0; k < 10; k++)
						{
							FileDate[k] = 0;
						}
						Filerecord++;
						recordPl++;
						printf("已找回!\n");
						continue;
					}
					//找到的节点不是头节点
					else
					{
						pT2->pNext = pTemp;
						pTemp->pNext = pT1;
						//清空,排除干扰
						for (k = 0; str1[k] != '\0'; k++)
						{
							str1[k] = 0;
						}
						for (k = 0; str3[k] != '\0'; k++)
						{
							str3[k] = 0;
						}
						for (k = 0; k < 10; k++)
						{
							FileDate[k] = 0;
						}
						Filerecord++;
						recordPl++;
						printf("已找回!\n");
						continue;
					}
				}
				//找不到比插入节点编号更大的
				else
				{
					pT1->pNext = pTemp;
					pTemp->pNext = NULL;
					g_pEnd = pTemp;
					//清空,排除干扰
					for (k = 0; str1[k] != '\0'; k++)
					{
						str1[k] = 0;
					}
					for (k = 0; str3[k] != '\0'; k++)
					{
						str3[k] = 0;
					}
					for (k = 0; k < 10; k++)
					{
						FileDate[k] = 0;
					}
					Filerecord++;
					recordPl++;
					printf("已找回!\n");
					continue;
				}

			}
		}
		//不是就继续读
		else
		{
			continue;
		}

	}
	if (Filerecord == 0)
		printf("你需要恢复的选手不存在,请检查名字是否输入错误\n");
}

struct PlayerNode* LookPointedPlayer_Rank(char* arrApointer)
{
	if (g_pHead == NULL && arrApointer == NULL)
	{
		printf("该链表没有节点或你输入的格式不对,请检查!\n");
		return NULL;
	}
	//记住头
	struct PlayerNode* pTemp = g_pHead;
	while (NULL != pTemp)
	{
		if (strcmp(pTemp->arrPlayerNumber, arrApointer) == 0)
		{
			printf("~~~~找到了~~~~\n");
			return pTemp;
		}
		pTemp = pTemp->pNext;
	}
	printf("~~~~没找到该编号对应的选手~~~~\n");
	return NULL;
}

double Average(struct PlayerNode* pTemp)
{
	double score[10] = { 0.0 };
	int i = 0, j = 0;
	double ScoreTemp, Scoresum = 0.0;
	for (i = 0; i < 10; i++)
	{
		score[i] = pTemp->dbstudent[i];
	}
	for (i = 0; i < 9; i++)
	{
		for (j = i + 1; j < 10; j++)
		{
			if (score[j] > score[i])
			{
				ScoreTemp = score[j];
				score[j] = score[i];
				score[i] = ScoreTemp;
			}
		}
	}
	for (i = 1; i < 9; i++)
	{
		Scoresum += score[i];
	}
	return Scoresum * 1.0 / 8;
}

void ExPlayerNode(struct PlayerNode* pNode1, struct PlayerNode* pNode2)
{
	char arrExchangeName[20] = { 0 };
	char arrExchangeNum[20] = { 0 };
	int i;
	double nTempscore;
	for (i = 0; i < 10; i++)                     //交换节点的分数
	{
		nTempscore = pNode1->dbstudent[i];
		pNode1->dbstudent[i] = pNode2->dbstudent[i];
		pNode2->dbstudent[i] = nTempscore;
	}
	//交换学号
	strcpy(arrExchangeNum, pNode1->arrPlayerNumber);
	strcpy(pNode1->arrPlayerNumber, pNode2->arrPlayerNumber);
	strcpy(pNode2->arrPlayerNumber, arrExchangeNum);
	//交换名字
	strcpy(arrExchangeName, pNode1->arrPlayername);
	strcpy(pNode1->arrPlayername, pNode2->arrPlayername);
	strcpy(pNode2->arrPlayername, arrExchangeName);
	return;
}



四:总结

说实话,这个项目还是有很多不足的,比如有些功能重复了,有些代码根本没必要,比如那个清空字符串的,完全可以弄成一个函数,还有代码的格式规范,我觉得都是一个很大的问题。但其中也有文件操作,链表操作,对于一个c语言的小项目够用了。如果你有什么看法,欢迎评论,也可以提出哪里有优化。

  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值