1.运用链表中的带头双向循环链表
描述:1.prev是前指针,next是后指针
2.优点:写起来简单无脑
3.顺便插一句:本人使用的是Vs2022编译环境
目录:
一.建立工程性文件
1.建立一个头文件,一个测试文件
2.将结构体定义,头文件以及函数声明等放在头文件中.
3.测试文件是用来放置main函数的
4.函数文件我的习惯一个函数创建一个文件
二.头文件展示
#define _CRT_SECURE_NO_WARNINGS//防止部分程序因不安全报错
#pragma once
//头文件
//该项目主要运用带头双向循环列表结构体实现
//带头双向循环列表属于单链表的一种,实用性强,可有效减少bug
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <easyx.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>//断言
#include <stdbool.h>//判断
//计划使用带头双向循环链表实现
//结构体定义
typedef int wallet;
typedef char Name,Num, Lost_Card;
typedef struct user_message {
//用户信息
Name name[20];//名字
Num number[10];//序号
wallet money;//余额
Lost_Card ch[5];//是否挂失
//头连接和尾连接
struct user_message* prve;
struct user_message* next;
}UM;
//函数接口(暂时不仔细思考函数返回类型可使用void替代)
//初始化并建立哨兵位(ok)
struct user_message*Init_Creat_sentry();
//菜单(ok)
void menu();
//控制系统(ok)
void Control_system(UM*head);
//创建新节点(ok)
struct user_message*NewNode(Name*name,Num*number,wallet money,Lost_Card*ch);
//1号->建立饭卡文件并数据写入(以什么标志结束输入(思考ing))(ok)
void Creat_user(UM*head);
//2号->买饭,输入金额,显示剩余金额(ok)
void Buy_Food(UM* head);
//3号->续钱,输入金额,显示剩余金额(ok)
void Top_up(UM* head);
//4号->添加新饭卡(插入数据)(ok)
void Push_NewCard(UM* head);
//5号->注销旧饭卡(删除数据)(ok)
void Pop_OldCard(UM* head);
//6号->设置以及解除挂失()(ok)
void Setting_Card(UM* head);
//7号->创新功能(ok)
void Index_CardMessage(UM* head);
//8号->文件读取学生信息(ok)
void Read_Message(UM* head);
//释放数据结构(ok)
void Destory_Prom(UM* head);
//打印所有数据
void Printf_All(UM* head);
//数据打印(ok)
void Print(UM*head);//用于测试
1.其实有一些头文件是用不上的,这里作者我本人怕那个地方突然用上而顺手写上去的.
2.在vs2022某些函数会因安全问题而报错,这里只需要在头文件添加顶部这句就可以避免大部分的安全报错.
3.在这里大家会发现我每个函数后面都会写个OK,或者用于测试这类的字眼,这是因为本人喜欢每写一个函数就测试一遍,直到能正常运作再去写另外一个函数
二.初始化并建立哨兵位(head)
#include "Rice_Card.h"
//创建哨兵位,也就是所谓的头,头是没有任何数据的,只要一个地址
struct user_message* Init_Creat_sentry(){
UM* phead = (UM*)malloc(sizeof(UM));
if (phead == NULL) {
printf("Creat Fail!\n");
exit(-1);
}
phead->prve = phead;
phead->next = phead;
return phead;
}
三.菜单
#include "Rice_Card.h"
//菜单
void menu() {
Sleep(100);
printf("---------------------------------\n"); Sleep(50);//设置下一个窗口运行时间为0.05毫秒之后
printf(" 饭卡管理系统 \n"); Sleep(50);
printf("\n"); Sleep(100);
printf("[1]建立饭卡文件 [2]买饭\n"); Sleep(50);
printf("[3]续钱 [4]添加新饭卡\n"); Sleep(50);
printf("[5]注销旧饭卡 [6]设置以及解除挂失\n"); Sleep(50);
printf("[0]退出系统 [7]查看饭卡具体信息\n"); Sleep(50);
printf("[8]从指定文件夹读取学生信息\n"); Sleep(50);
printf("[9]打印所有饭卡信息\n"); Sleep(50);
printf("---------------------------------\n"); Sleep(50);
}
这里的Sleep()是休眠函数,比如Sleep(50);就是让系统休眠50毫秒之后再执行下一个语句,这样就可以达到一种动态输入的感觉;
四.控制系统
#include "Rice_Card.h"
void Control_system(UM* head) {
assert(head);
printf("请输入你要执行程序的序号!\n");
int num=0;
while(scanf("%d", &num) == 1) {
switch (num) {
case 1:Creat_user(head); break;
case 2:Buy_Food(head); break;
case 3:Top_up(head); break;
case 4:Push_NewCard(head); break;
case 5:Pop_OldCard(head); break;
case 6:Setting_Card(head); break;
case 7://system("cls");//实现清屏效果
Index_CardMessage(head); break;
case 0:printf("感谢使用本软件!已正常退出!\n");
Print(head);
system("pause");//任意键退出(只是暂时停止运行)指令
exit(0);//exit(0)表示正常退出
case 8:Read_Message(head); break;
case 9:Printf_All(head); break;
default:printf("请输入正确的序号!\n"); break;
}
printf("请输入你要执行程序的序号!\n");
}
}
五.创建新节点
#include "Rice_Card.h"
struct user_message* NewNode(Name* name, Num*number, wallet money, Lost_Card*ch){
UM* newnode = (UM*)malloc(sizeof(UM));
//信息读取
if (newnode == NULL) {
printf("Creat Fail!\n");
}
else {
strcpy(newnode->name, name);
strcpy(newnode->number, number);
strcpy(newnode->ch, ch);
newnode->money = money;
//设置连接
newnode->prve = NULL;
newnode->next = NULL;
}
return newnode;
}
六.1号->建立饭卡文件并数据写入
#include "Rice_Card.h"
UM* cur = NULL;
FILE* fp;
void Creat_user(UM* head)
{
assert(head);
//FILE* fp;
char Over[20] = { "结束" };
//循环条件
//信息输入文件
if ((fp = fopen("student_message.txt", "ab+")) == NULL)
{
printf("Open the File Fail!\n");
exit(-1);
}
else
{
if (head->next == head)
{ //if中写成"="不会报错请自己好好看看
cur = head;
}
else {
cur = head->prve;
}
while (1)
{
Name name[20] = { NULL };
Num number[10] = { NULL };
wallet money = 0;
Lost_Card ch[5] = { NULL };
printf("请输入用户名字(在此输入结束时该程序退出):");
if (scanf("%s", name) == 1)
{
//结束条件(待改进)
if ((strcmp(Over, name)) == 0)
{
break;
}
fprintf(fp, "%s\t", name);
}
printf("请输入用户序号:");
if (scanf("%s", number) == 1)
{
//实现序号唯一性
UM* next = head->next;
if (next == NULL)return;
while (next!=head)
{
if (cur != NULL && (strcmp(next->number, number) == 0))
{
printf("重新输入序号:");
memset(number, 0, sizeof(char));
scanf_s("%s", number, 10);
next = head->next;
}
else {
if (next == NULL) return;
next = next->next;
}
}
fprintf(fp, "%s\t", number);
}
printf("请输入用户账户金额:");
if (scanf("%d", &money) == 1)
{
fprintf(fp, "%d\t", money);
}
printf("请输入是否挂失饭卡!(填入是或否):");
if (scanf("%s", ch) == 1)
{
fprintf(fp, "%s\n", ch);
}
//存入结构体中
UM* newnode = NewNode(name, number, money, ch);
if (newnode == NULL)
{
printf("存入失败!\n");
exit(1);
}
else
{
if (cur == NULL)
return;
cur->next = newnode;
head->prve = newnode;
newnode->next = head;
newnode->prve = cur;
cur = newnode;
}
}
}
fclose(fp);
}
这里主要实现的是从屏幕上输入信息到文件当中,并且将其在双向循环链表中连接起来
七.2号->买饭,输入金额,显示剩余金额
#include "Rice_Card.h"
void Buy_Food(UM* head) {
assert(head);
Num number[10] = { NULL };
int price=0;
int count = 0;//计算定位饭卡的位置
//输入卡号,输入饭钱
printf("请输入卡号:");
scanf_s("%s", &number, 10);
printf("请输入饭钱:");
scanf_s("%d", &price);
//查询饭卡信息(定位)
UM* cur = head->next;
if (cur == NULL) {
printf("没有任何数据!\n");
exit(1);
}
while (strcmp(number, cur->number) != 0) {
if (cur == head) {
printf("该饭卡不存在,请输入正确的饭卡号:");
scanf_s("%s", &number, 10);
count = 0;
}
count++;
cur = cur->next;
}
//实现题目要求情况
if (cur->money < 5) {
printf("余额不足,请续钱\n");
return;
}
if (strcmp(cur->ch, "是") == 0) {
printf("本卡已冻结!\n");
return;
}
printf("买饭前:%d\n",cur->money);
cur->money = cur->money - price;
printf("买饭后:%d\n",cur->money);
extern FILE* fp;//外部文件全局变量
//文件部分修改
//创建临时文件
FILE* tp;
if ((tp = fopen("tp.txt", "wb+")) == NULL) {
printf("fopen tp fail!\n");
exit(-1);
}
//创建保存数据
char *str = (char*)malloc(sizeof(char) * 100);
//打开原先文件
if ((fp = fopen("student_message.txt", "ab+")) == NULL) {
printf("Open the File Fail!\n");
exit(-1);
}
fseek(fp, 0, SEEK_SET);//首部读取数据
//保存修改前的数据
while (count>0) {
memset(str, 0, sizeof(str));
fgets(str, 100, fp);
fputs(str, tp);
count--;
}
//细节修改
char target[40] = { NULL };
char ch = NULL;
int i = 0;
int m = 0;
while (!feof(fp)&&m<2) {
ch = fgetc(fp);
target[i++] = ch;
if (ch == '\t')m++;
}
target[i++] = '\0';//手动添加0
fputs(target, tp);
fprintf(tp,"%d\t",cur->money);//读取修改数据
//该行读取剩下数据
memset(target, 0, sizeof(target));
while (!feof(fp)) {
ch = fgetc(fp);
if (ch == '\t') {
fgets(target,40, fp);
fputs(target, tp);
break;
}
}
//读取剩余数据
while (!feof(fp)) {
memset(str, 0, sizeof(str));//一定要记得清空
fgets(str, 100, fp);
fputs(str, tp);
}
fclose(tp);
fclose(fp);
//删除原文件,临时文件改名
remove("student_message.txt");
//old name newname
if (rename("tp.txt", "student_message.txt") == -1) {
printf("修改失败!\n");
exit(-1);
}
}
八.3号->续钱,输入金额,显示剩余金额
#include "Rice_Card.h"
void Top_up(UM* head) {
assert(head);
Num number[10] = { NULL };
int add_money = 0;
int count = 0;//计算定位饭卡的位置
//输入卡号,输入饭钱
printf("请输入卡号:");
scanf_s("%s", &number, 10);
printf("请输入增加的钱:");
scanf_s("%d", &add_money);
//查询饭卡信息(定位)
UM* cur = head->next;
if (cur == NULL) {
printf("没有任何数据!\n");
exit(1);
}
while (strcmp(number, cur->number) != 0) {
if (cur == head) {
printf("该饭卡不存在,请输入正确的饭卡号:");
scanf_s("%s", &number, 10);
count = 0;
}
count++;
cur = cur->next;
}
//自己添加
if (strcmp(cur->ch, "是") == 0) {
printf("本卡已冻结!\n");
return;
}
printf("买饭前:%d\n", cur->money);
cur->money = cur->money + add_money;
printf("买饭后:%d\n", cur->money);
extern FILE* fp;//外部文件全局变量
//文件部分修改
//创建临时文件
FILE* tp;
if ((tp = fopen("tp.txt", "wb+")) == NULL) {
printf("fopen tp fail!\n");
exit(-1);
}
//创建保存数据
char* str = (char*)malloc(sizeof(char) * 100);
//打开原先文件
if ((fp = fopen("student_message.txt", "ab+")) == NULL) {
printf("Open the File Fail!\n");
exit(-1);
}
fseek(fp, 0, SEEK_SET);//首部读取数据
//保存修改前的数据
while (count > 0) {
memset(str, 0, sizeof(str));
fgets(str, 100, fp);
fputs(str, tp);
count--;
}
//细节修改
char target[40] = { NULL };
char ch = NULL;
int i = 0;
int m = 0;
while (!feof(fp) && m < 2) {
ch = fgetc(fp);
target[i++] = ch;
if (ch == '\t')m++;
}
target[i++] = '\0';//手动添加0
fputs(target, tp);
fprintf(tp, "%d\t", cur->money);//读取修改数据
//该行读取剩下数据
memset(target, 0, sizeof(target));
while (!feof(fp)) {
ch = fgetc(fp);
if (ch == '\t') {
fgets(target, 40, fp);
fputs(target, tp);
break;
}
}
//读取剩余数据
while (!feof(fp)) {
memset(str, 0, sizeof(str));//一定要记得清空
fgets(str, 100, fp);
fputs(str, tp);
}
最后一步将临时文件复制到原文件
//fseek(tp, 0, SEEK_SET);
//fseek(fp, 0, SEEK_SET);
//while (!feof(tp)) {
// memset(str, 0, sizeof(str));
// fgets(str, 100, tp);
// fputs(str, fp);
//}
fclose(tp);
fclose(fp);
//删除原文件,临时文件改名
remove("student_message.txt");
//old name newname
if (rename("tp.txt", "student_message.txt") == -1) {
printf("修改失败!\n");
exit(-1);
}
}
九.4号->添加新饭卡
#include "Rice_Card.h"
void Push_NewCard(UM* head) {
UM* cur = head->prve;
UM* Cur = head->next;
assert(head);
FILE* fp;
char Over[20] = { "结束" };
if ((fp = fopen("card.dat", "ab+")) == NULL) {
printf("Open the File Fail!\n");
exit(-1);
}
else {
fseek(fp, 0, SEEK_END);
Name name[20] = { NULL };
Num number[10] = { NULL };
wallet money = 0;
Lost_Card ch[5] = { NULL };
printf("请输入用户名字(在此输入结束时该程序退出):");
if (scanf("%s", name) == 1) {
fprintf(fp, "%s\n", name);
}
printf("请输入用户序号:");
if (scanf("%s", number) == 1) {
//实现序号唯一性
while (Cur != head) {
if (strcmp(number, Cur->number) == 0) {
printf("用户已存在,请重新输入序号:");
scanf_s("%s", &number, 10);
Cur = head;
}
Cur = Cur->next;
}
fprintf(fp, "%s\n", number);
}
printf("请输入用户账户金额:");
if (scanf("%d", &money) == 1) {
fprintf(fp, "%d\n", money);
}
printf("请输入是否挂失饭卡!(填入是或否):");
if (scanf("%s", ch) == 1) {
fprintf(fp, "%s\n", ch);
}
//存入结构体中
UM* newnode = NewNode(name, number, money, ch);
if (newnode == NULL) {
printf("存入失败!\n");
exit(1);
}
else {
if (cur == NULL) {//if中写成"="不会报错请自己好好看看
cur = head;
}
if (cur == NULL)return;
cur->next = newnode;
head->prve = newnode;
newnode->next = head;
newnode->prve = cur;
cur = newnode;
}
}
}
十.5号->注销旧饭卡
#include"Rice_Card.h"
void Pop_OldCard(UM* head) {
assert(head);
Num number[10] = { NULL };
printf("填写需要注销饭卡序号:");
scanf_s("%s", number, 10);
int count = 0;
//查询饭卡信息(定位)
UM* cur = head->next;
if (cur == NULL) {
printf("没有任何数据!\n");
exit(1);
}
while (strcmp(number, cur->number) != 0) {
if (cur == head) {
printf("该饭卡不存在,请输入正确的饭卡号:");
scanf_s("%s", &number, 10);
count = 0;
}
count++;
cur = cur->next;
}
//删除
cur->prve->next = cur->next;
cur->next->prve = cur->prve;
//文件删除部分
//创建临时文件
FILE* tp;
if ((tp = fopen("tp.txt", "wb+")) == NULL) {
printf("fopen tp fail!\n");
exit(-1);
}
extern FILE* fp;
//创建保存数据
char* str = (char*)malloc(sizeof(char) * 100);
//打开原先文件
if ((fp = fopen("student_message.txt", "ab+")) == NULL) {
printf("Open the File Fail!\n");
exit(-1);
}
fseek(fp, 0, SEEK_SET);//首部读取数据
//保存修改前的数据
while (count > 0) {
memset(str, 0, sizeof(str));
fgets(str, 100, fp);
fputs(str, tp);
count--;
}
//舍弃数据
char target[100];
char ch = NULL;
memset(target, 0, sizeof(target));
while (!feof(fp)) {
fgets(target, 100, fp);
break;
}
//读取剩余数据
while (!feof(fp)) {
memset(str, 0, sizeof(str));//一定要记得清空
fgets(str, 100, fp);
fputs(str, tp);
}
fclose(tp);
fclose(fp);
//删除原文件,临时文件改名
remove("student_message.txt");
//old name newname
if (rename("tp.txt", "student_message.txt") == -1) {
printf("修改失败!\n");
exit(-1);
}
}
十一.6号->设置以及解除挂失
#include"Rice_Card.h"
void Setting_Card(UM* head) {
assert(head);
Num number[10] = { NULL };
printf("填写需要更改信息的饭卡序号:");
scanf_s("%s", number, 10);
int count = 0;
//查询饭卡信息(定位)
UM* cur = head->next;
if (cur == NULL) {
printf("没有任何数据!\n");
exit(1);
}
while (strcmp(number, cur->number) != 0) {
if (cur == head) {
printf("该饭卡不存在,请输入正确的饭卡号:");
scanf_s("%s", &number, 10);
count = 0;
}
count++;
cur = cur->next;
}
//修改挂失情况
Lost_Card Ch[5] = {NULL};
printf("输入饭卡挂失情况:(是则挂失,否则恢复正常使用)\n");
scanf_s("%s", Ch, 5);
strcpy(cur->ch, Ch);
extern FILE* fp;//外部文件全局变量
//文件部分修改
//创建临时文件
FILE* tp;
if ((tp = fopen("tp.txt", "wb+")) == NULL) {
printf("fopen tp fail!\n");
exit(-1);
}
//创建保存数据
char* str = (char*)malloc(sizeof(char) * 100);
//打开原先文件
if ((fp = fopen("student_message.txt", "ab+")) == NULL) {
printf("Open the File Fail!\n");
exit(-1);
}
fseek(fp, 0, SEEK_SET);//首部读取数据
//保存修改前的数据
while (count > 0) {
memset(str, 0, sizeof(str));
fgets(str, 100, fp);
fputs(str, tp);
count--;
}
//细节修改
char target[100] = { NULL };
char ch = NULL;
int i = 0;
int m = 0;
while (!feof(fp) && m < 3) {
ch = fgetc(fp);
target[i++] = ch;
if (ch == '\t')m++;
}
target[i++] = '\0';//手动添加0
fputs(target, tp);
fprintf(tp, "%s", cur->ch);//读取修改数据
//舍弃最后的数据
memset(target, 0, sizeof(target));
while (!feof(fp)) {
ch = fgetc(fp);
if (ch == '\n') {
fprintf(tp,"%c", ch);
break;
}
}
//读取剩余数据
while (!feof(fp)) {
memset(str, 0, sizeof(str));//一定要记得清空
fgets(str, 100, fp);
fputs(str, tp);
}
最后一步将临时文件复制到原文件
//fseek(tp, 0, SEEK_SET);
//fseek(fp, 0, SEEK_SET);
//while (!feof(tp)) {
// memset(str, 0, sizeof(str));
// fgets(str, 100, tp);
// fputs(str, fp);
//}
fclose(tp);
fclose(fp);
//删除原文件,临时文件改名
remove("student_message.txt");
//old name newname
if (rename("tp.txt", "student_message.txt") == -1) {
printf("修改失败!\n");
exit(-1);
}
}
十二.查询信息
#include "Rice_Card.h"
//创新功能,通过输入序号查询,饭卡的所有信息
void Index_CardMessage(UM* head) {
assert(head);
Num number[10] = { NULL };
printf("填写查看信息的饭卡序号:");
scanf_s("%s", number, 10);
//查询饭卡信息(定位)
UM* cur = head->next;
if (cur == NULL) {
printf("没有任何数据!\n");
exit(1);
}
while (strcmp(number, cur->number) != 0) {
cur = cur->next;
if (cur == head) {
printf("该饭卡不存在,请输入正确的饭卡号:");
scanf_s("%s", &number, 10);
}
}
printf("饭卡持有者:\t%s\n", cur->name);
printf("饭卡序号:\t%s\n", cur->number);
printf("饭卡剩余金额:\t%d\n", cur->money);
printf("饭卡挂失情况:\t%s\n", cur->ch);
}
十三.8号->文件读取学生信息
#include "Rice_Card.h"
void Read_Message(UM* head) {
assert(head);
//设置文件
extern FILE*fp;
fp = fopen("student_message.txt", "ab+");
if (fp == NULL) {
printf("Cannot open the fail!\n");
exit(-1);
}
UM* cur = NULL;
//读取文件并保存
while (!feof(fp)) {
//创建新节点保存文件
UM* Newnode = (UM*)malloc(sizeof(UM));
if (Newnode == NULL) {
printf("Creat newnode fail!\n");
exit(-1);
}
memset(Newnode->name, 0, sizeof(Newnode->name));
memset(Newnode->number, 0, sizeof(Newnode->number));
memset(Newnode->ch, 0, sizeof(Newnode->ch));
Newnode->money = NULL;
if (fscanf(fp, "%s\t%s\t%d\t%s\n", Newnode->name, Newnode->number, &Newnode->money, Newnode->ch) == EOF) {
printf("无法写入!");
exit(-1);
};
UM* newnode = NewNode(Newnode->name, Newnode->number, Newnode->money, Newnode->ch);
//连接结点
if (head->next == head)
{ //if中写成"="不会报错请自己好好看看
cur = head;
}
else {
cur = head->prve;
}
if (cur == NULL)
return;
cur->next = newnode;
head->prve = newnode;
newnode->next = head;
newnode->prve = cur;
cur = newnode;
}
fclose(fp);
}
十四.释放数据结构
#include "Rice_Card.h"
//销毁链表,在每次运行结束后都应该养成释放链表的习惯
void Destory_Prom(UM* head) {
assert(head);
if (head->next != head) {
UM* cur = head->next;
UM* next = NULL;
while (cur != head) {
next = cur->next;
free(cur);
cur = next;
}
head = NULL;
cur = NULL;
next = NULL;
}
}
十五.打印所有数据
#include "Rice_Card.h"
void Printf_All(UM* head) {
UM* cur = head->next;
while (cur != head) {
printf("饭卡持有者:\t%s\n", cur->name);
printf("饭卡序号:\t%s\n", cur->number);
printf("饭卡剩余金额:\t%d\n", cur->money);
printf("饭卡挂失情况:\t%s\n", cur->ch);
printf("\n");
cur = cur->next;
}
}
十六.数据打印(测试)
#include "Rice_Card.h"
void Print(UM* head) {
if (head->next == head) {
printf("NULL");
}
else {
assert(head);
UM* cur = head->next;
while (cur != NULL && cur != head) {
printf("%d->", cur->money);
cur = cur->next;
}
printf("NULL\n");
}
}//用于测试
十七.测试文件(主函数)
#include "Rice_Card.h"
//运行函数
//所有函数都会在main函数中运行,所以在执行文件时会按顺序开始执行
int main(void) {
system("color e3\n");//设置字体颜色以及输入屏幕颜色
menu();
extern FILE* fp;
UM* head =Init_Creat_sentry();
Control_system(head);
Destory_Prom(head);
return 0;
}
总结.
假如你能认真的看下来你会发现我的代码是有许多累赘的,比如饭卡的定位,很多地方都会用到但我却没有另起一个函数,以及文件的删除,节点的连接等都是如此.所以说,大家在观看这个代码后希望大家可以写一个更好,更轻便的代码.
感谢你的观看!