c/c++公交管理系统(星穹轨道可视化EasyX)

  注:全代码由博主个人耗时两个星期开发,实现了基础的公交管理系统功能:1.前端用户查询站点以及查看所有站点。2.后端管理员权限,实现登录系统检验,添加路线以及删除路线。最重要的是使用EasyX实现了星穹轨道的启动以及抽卡界面以及音效

注意:代码可以直接拿,但是可视化部分的素材需收费(博主做可视化部分就做了一个多星期),需要的可以直接看文章结尾。

话不多说直接上代码:

#define  _CRT_SECURE_NO_WARNINGS
/*开发日志
* 1.明确公交管理系统分为后台和前台
* 2.前台的主要功能是给用户规划最佳路线
* 3.后台的主要功能是给管理员添加和删除路线使用
* 使用技术:
* 1.数据处理
*   1.1去重以及划分路线范围(路线范围后加)
*   1.2给每个站点进行编号便于处理
* 2.进行图的建立
* 3.使用Dijkstra算法给出最短路径
*
* 日志12-21 准备可视化开发
* 基本核心算法已经就绪
* 12-25开场动画准备完毕,开始进入第二阶段,查询功能判定
* 判断写好了接下来就是最后的输出了
* 遇到困难了,发现无法读取中文
* 好的困难解决将整个程序改为了unioncode字符集完成开发
* 又遇到难点了,怎么在文本框里换行输入啊,算了直接多设置几个控件就好了
* 好的管理员权限开发完毕
* 1-1开始开发使用者权限
* 1-2上午完成查询站点开发,使用了string转为wchar_t*的技术
* 
* 
* 与1-2晚上完成开发,完美收工!!!!!!
*/
#include<fstream>
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<graphics.h>
#include<conio.h>
#include <Windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
using namespace std;#define WHIDE 1870
#define HEIGHT 1000
//我们采用邻接表的方式储存数据
typedef struct First_Situation First_Situation;   //数组类型
typedef struct situation situation;  //小节点
typedef struct graph Graph;          //图
typedef struct Road  Road;           //线路// 实现文本框控件
class EasyTextBox
{
private:int left = 0, top = 0, right = 0, bottom = 0;	// 控件坐标wchar_t* text = NULL;							// 控件内容size_t maxlen = 0;									// 文本框最大内容长度
public:void Create(int x1, int y1, int x2, int y2, int max){maxlen = max;text = new wchar_t[maxlen];text[0] = 0;left = x1, top = y1, right = x2, bottom = y2;// 绘制用户界面Show();}~EasyTextBox(){if (text != NULL)delete[] text;}wchar_t* Text(){return text;}bool Check(int x, int y){return (left <= x && x <= right && top <= y && y <= bottom);}// 绘制界面void Show(){// 备份环境值int oldlinecolor = getlinecolor();int oldbkcolor = getbkcolor();int oldfillcolor = getfillcolor();setlinecolor(LIGHTGRAY);		// 设置画线颜色setbkcolor(0xeeeeee);			// 设置背景颜色setfillcolor(0xeeeeee);			// 设置填充颜色fillrectangle(left, top, right, bottom);settextstyle(50, 0, L"黑体");outtextxy(left + 10, top + 5, text);// 恢复环境值setlinecolor(oldlinecolor);setbkcolor(oldbkcolor);setfillcolor(oldfillcolor);}void OnMessage(){// 备份环境值int oldlinecolor = getlinecolor();int oldbkcolor = getbkcolor();int oldfillcolor = getfillcolor();setlinecolor(BLACK);			// 设置画线颜色setbkcolor(WHITE);				// 设置背景颜色setfillcolor(WHITE);			// 设置填充颜色fillrectangle(left, top, right, bottom);outtextxy(left + 10, top + 5, text);int width = textwidth(text);	// 字符串总宽度int counter = 0;				// 光标闪烁计数器bool binput = true;				// 是否输入中ExMessage msg;while (binput){while (binput && peekmessage(&msg, EX_MOUSE | EX_CHAR, false))	// 获取消息,但不从消息队列拿出{if (msg.message == WM_LBUTTONDOWN){// 如果鼠标点击文本框外面,结束文本输入if (msg.x < left || msg.x > right || msg.y < top || msg.y > bottom){binput = false;break;}}else if (msg.message == WM_CHAR){size_t len = wcslen(text);switch (msg.ch){case '\b':				// 用户按退格键,删掉一个字符if (len > 0){text[len - 1] = 0;width = textwidth(text);counter = 0;clearrectangle(left + 10 + width, top + 1, right - 1, bottom - 1);}break;case '\r':				// 用户按回车键,结束文本输入case '\n':binput = false;break;default:				// 用户按其它键,接受文本输入if (len < maxlen - 1){text[len++] = msg.ch;text[len] = 0;clearrectangle(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);	// 清除画的光标width = textwidth(text);				// 重新计算文本框宽度counter = 0;outtextxy(left + 10, top + 5, text);		// 输出新的字符串}}}peekmessage(NULL, EX_MOUSE | EX_CHAR);				// 从消息队列抛弃刚刚处理过的一个消息}// 绘制光标(光标闪烁周期为 20ms * 32)counter = (counter + 1) % 32;if (counter < 16) {line(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);				// 画光标}elseclearrectangle(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);		// 擦光标// 延时 20msSleep(20);}clearrectangle(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);	// 擦光标// 恢复环境值setlinecolor(oldlinecolor);setbkcolor(oldbkcolor);setfillcolor(oldfillcolor);Show();}
};struct situation {int number;situation* next;
};struct First_Situation {string name;int number;situation* firstnode;
};struct graph {First_Situation s[200];  //站点数组int sum;                //站点总数
};struct Road {int sum;                //该线路上的站点总数int zhandian[100];      //用数组表示站点编号string name;            //也就是这条路叫什么
};IMAGE sucai1;               //第一素材
IMAGE sucai2;               //第二素材
IMAGE sucai3;               //第三素材
IMAGE sucai4;               //第四素材
IMAGE sucai5;               //第五素材
IMAGE sucai6;               //第六素材
IMAGE sucai7;               //第七素材 
IMAGE sucai8;               //第八素材 
IMAGE first_donghua[150];   //第一动画   
IMAGE second_donghua[128];  //第二动画
IMAGE third_donghua[72];     //第三动画
IMAGE four_donghua[150];     //第四动画
Graph G;              //将图设计为全局变量
int Road_Sum;         //线路总数
map<string, int> m;   //这个是为了去重操作,并且直接找到站点对应的编号
vector<Road>  v;      //这个之所以用vector容器是为了之后便于删除路线
int parent[500];
int dist[500];
bool Visit[500];
int YS;              //用来判断账户密码是否是对的//函数区
int CountRoads();     //统计有多少条路线
int CountLines();     //统计有多少行
void init();          //初始化
void Creat_Graph();   //创建图
bool next(int x, int y);          //判断两点是否相连
int findmindist(int dist[], bool visit[]);      //找出最小路径
void Dijkstra(int x, int dist[], int parent[], bool visit[]);  //开始计算
void printmy(int x, int y, int parent[], int dist[]);    //打印结果
void choice1();                                          //管理员权限
void choice2();                                          //用户权限
int Insert_Road();                                      //增加路线
int Delete_Road();                                      //删除路线
int check_in(char zhanghao[], char mima[]);                                     //检查登录函数//可视化函数
void init_background();                             //图像初始化
void bofang1();                                     //第一播放动画
char bofang2();                                     //第二播放动画
void bofang3();                                     //第三播放动画
void bofang4();                                     //第四播放动画
char* wideCharToMultiByte(wchar_t* pWCStrKey);     //转化函数
wchar_t* multiByteToWideChar(const string& pKey);  //转化函数//用来将wchar_t*转为string
char* wideCharToMultiByte(wchar_t* pWCStrKey) {//第一次调用确认转换后单字节字符串的长度,用于开辟空间int pSize = WideCharToMultiByte(CP_OEMCP, 0, pWCStrKey, wcslen(pWCStrKey), NULL, 0, NULL, NULL);char* pCStrKey = new char[pSize + 1];//第二次调用将双字节字符串转换成单字节字符串WideCharToMultiByte(CP_OEMCP, 0, pWCStrKey, wcslen(pWCStrKey), pCStrKey, pSize, NULL, NULL);pCStrKey[pSize] = '\0';return pCStrKey;
}//将string转为wchar_t*
wchar_t* multiByteToWideChar(const string& pKey)
{const char* pCStrKey = pKey.c_str();//第一次调用返回转换后的字符串长度,用于确认为wchar_t*开辟多大的内存空间int pSize = MultiByteToWideChar(CP_OEMCP, 0, pCStrKey, strlen(pCStrKey) + 1, NULL, 0);wchar_t* pWCStrKey = new wchar_t[pSize];//第二次调用将单字节字符串转换成双字节字符串MultiByteToWideChar(CP_OEMCP, 0, pCStrKey, strlen(pCStrKey) + 1, pWCStrKey, pSize);return pWCStrKey;
}//具体实现
//统计文件行数,用来统计有多少个站点
int CountLines()
{ifstream ReadFile;int n = 0;string tmp;ReadFile.open("站点信息.txt", ios::in);//ios::in 表示以只读的方式读取文件if (ReadFile.fail())//文件打开失败:返回0{return 0;}else//文件存在{while (getline(ReadFile, tmp, '\n')){n++;}ReadFile.close();return n;}
}//统计有多少条路线
int CountRoads() {ifstream ReadFile;int n = 0;string tmp;ReadFile.open("路线信息.txt", ios::in);//ios::in 表示以只读的方式读取文件if (ReadFile.fail())//文件打开失败:返回0{return 0;}else//文件存在{while (getline(ReadFile, tmp, '\n')) {if (tmp.compare("end") == 0)n++;}ReadFile.close();}return n;
}//初始化开头动画
void init_background() {initgraph(WHIDE, HEIGHT, EX_SHOWCONSOLE);//显示控制台//加载背景资源wchar_t name[64];for (int i = 0; i < 150; i++) {swprintf(name,L"开始背景/背景 (%d).jpg",i+1 );loadimage(&first_donghua[i],name);}for (int i = 0; i < 128; i++) {swprintf(name,L"第二背景/背景s (%d).jpg", i + 1);loadimage(&second_donghua[i], name);}for (int i = 0; i < 72; i++) {swprintf(name,L"第三背景/背景t (%d).jpg", i + 1);loadimage(&third_donghua[i], name);}for (int i = 0; i < 150; i++) {swprintf(name, L"第四背景/背景 (%d).jpg", i + 1);loadimage(&four_donghua[i], name);}loadimage(&sucai1, L"素材库/素材1.jpg");loadimage(&sucai2, L"素材库/素材2.png");loadimage(&sucai3, L"素材库/素材3.png");loadimage(&sucai4, L"素材库/素材4.png");loadimage(&sucai5, L"素材库/素材5.png"); loadimage(&sucai6, L"素材库/素材6.png");loadimage(&sucai7, L"素材库/素材7.png");loadimage(&sucai8, L"素材库/素材8.png");
}void bofang1() {mciSendString(L"play 素材库/背景1.mp3 repeat", 0, 0, 0);int i = 0;int towards = 1;                      //方向,控制图片移动setbkmode(TRANSPARENT);settextcolor(WHITE);settextstyle(30, 0, L"黑体");while (1) {BeginBatchDraw();putimage(0, 0, &first_donghua[i]);putimage(720, 875, &sucai1);outtextxy(825, 800, L"按任意键进入程序");EndBatchDraw();if (i < 150 && towards == 1)i++;else if (i == 150) {towards = 0;i = 149;}else if (towards == 0 && i > 0)i--;else if (towards == 0 && i == 0) {i = 1;towards = 1;}Sleep(60);if (kbhit()) {getch();break;}}
}//在这里面我们需要多设一个变量来返回具体的选择
char bofang2() {char choice;int i = 0;int towards = 1;settextcolor(WHITE);int flag = 0;//开关1int flag2 = 1;//开关2int flag3 = 0;//开关3int flag4 = 0;//开关4int m = 0;   //开关登录检测int sum1 = 0;  //第一次读入了多少字符int sum2 = 0; //第二次读入了多少字符int a1 = 1;   //控制读取给哪个int a2 = 0;int k = 0;char text1[60] = { '\0' };  //账号char text2[60] = { '\0' };  //密码while (1) {settextstyle(50, 0, L"黑体");BeginBatchDraw();putimage(0, 0, &second_donghua[i]);if (i == 42)mciSendString(L"play 素材库/列车之启.mp3", 0, 0, 0);if (i > 70 && flag == 0) {outtextxy(100, 100, L"1.管理员权限");outtextxy(100, 300, L"2.使用者权限");settextstyle(30, 0,L"黑体");outtextxy(100, 500, L"按下1或2即可使用");}if (flag == 1) {settextstyle(35, 0, L"黑体");settextcolor(BLACK);putimage(550, 250, &sucai2);if (flag2 == 1) {outtextxy(610, 410, L"请在此输入您的账号");}if (flag3 == 1) {outtextxy(610, 490, L"请在此输入您的密码");}if (flag4 == 1) {settextstyle(20, 0, L"宋体");settextcolor(BLACK);outtextxy(610, 550, L"账号或着密码错误,请重试");outtextxy(610, 600, L"按Y重新开始");if (kbhit()) {if (getch() == 'Y') {break;}}}if (a1 == 1 && a2 == 0) {//这里我们需要循环打印之前的内容for (int j = 0; j < k; j++) {outtextxy(610 + j * 17, 410, text1[j]);}char ch;if (kbhit()) {flag2 = 0;ch = getch();if (ch == 13) {flag3 = 1;a1 = 0;a2 = 1;sum1 = k;k = 0;}else if (ch == 8) {if (k > 0) {text1[k] = '\0';k--;}}else if (ch >= 32 && ch <= 126) {if (k < 60) {text1[k] = ch;k++;}}}}else if (a1 == 0 && a2 == 1 && flag4 == 0) {for (int j = 0; j < sum1; j++) {outtextxy(610 + j * 17, 410, text1[j]);}for (int j = 0; j < k; j++) {outtextxy(610 + j * 17, 490, text2[j]);}char ch;if (kbhit()) {flag3 = 0;ch = getch();if (ch == 13) {m = 1;}else if (ch == 8) {if (k > 0) {text2[k] = '\0';k--;}}else if (ch >= 32 && ch <= 126) {if (k < 60) {text2[k] = ch;k++;}}}}}EndBatchDraw();//这一块是为了循环播放背景图片if (i < 127 && towards == 1)i++;else if (i == 127) {towards = 0;i = 126;}else if (towards == 0 && i > 75)i--;else if (towards == 0 && i == 75) {i = 76;towards = 1;}Sleep(60);if (m == 1) {if (check_in(text1, text2) == 1) {bofang3();YS = 1;return choice;}else {flag4 = 1;YS = 0;}}if (flag != 1) {if (kbhit()) {char ch = getch();choice = ch;     //返回值if (ch == '1')flag = 1;else if (ch == '2') {bofang3();return choice;}}}}
}void bofang3() {int i = 0;while (i < 72) {putimage(0, 0, &third_donghua[i]);Sleep(60);if (i == 8) {mciSendString(L"play 素材库/开动.mp3", 0, 0, 0);}i++;}mciSendString(L"stop 素材库/背景1.mp3", 0, 0, 0);
}void bofang4() {mciSendString(L"play 素材库/抽卡.mp3", 0, 0, 0);for (int i = 0; i < 150; i++) {putimage(0, 0, &four_donghua[i]);//这里我们加一个一键跳过的功能if (kbhit()) {char ch = getch();if (ch == ' ') {mciSendString(L"stop 素材库/抽卡.mp3", 0, 0, 0);break;}}Sleep(100);}
}//初始化
void init() {G.sum = CountLines();Road_Sum = CountRoads();ifstream  fin;fin.open("站点信息.txt", ios::in);if (!fin){std::cerr << "cannot open the file";}for (int i = 0; i < G.sum; i++) {int a;string s;fin >> a >> s;m.insert(pair<string, int>(s, a));G.s[i].number = a;G.s[i].name = s;}fin.close();
}//创建图
void Creat_Graph() {ifstream  fin;fin.open("路线信息.txt", ios::in);if (!fin){std::cerr << "cannot open the file";}for (int i = 0; i < Road_Sum; i++) {int sum = 0;Road r;r.sum = 0;fin >> r.name;string t;fin >> t;while (t.compare("end") != 0) {map<string, int>::iterator iter;iter = m.find(t);if (iter != m.end()) {r.zhandian[sum++] = iter->second;fin >> t;}}r.sum = sum;//结束之后直接开始点之间相连for (int j = 0; j < r.sum - 1; j++) {int a1 = r.zhandian[j];int a2 = r.zhandian[j + 1];situation* s1 = (situation*)malloc(sizeof(situation));situation* s2 = (situation*)malloc(sizeof(situation));s2->number = G.s[a2].number;s1->number = G.s[a1].number;s2->next = G.s[a1].firstnode;G.s[a1].firstnode = s2;s1->next = G.s[a2].firstnode;G.s[a2].firstnode = s1;}v.push_back(r); //将路线添加到容器里}fin.close();
}bool next(int x, int y) {   //为了判断这两个点是否相连situation* p;for (p = G.s[x].firstnode; p; p = p->next) {if (p->number == y)return true;}return false;
}int findmindist(int dist[], bool visit[]) {    //找最小的路径int minv, v;int mindist = 999;for (v = 0; v < G.sum; v++) {if (visit[v] == false && dist[v] < mindist) {mindist = dist[v];minv = v;}}if (mindist < 999) {return minv;}elsereturn -1;//表示不存在
}void Dijkstra(int x, int dist[], int parent[], bool visit[])    //Dijkstra算法,传入源顶点
{int v, w;situation* m;//初始化for (int i = 0; i < G.sum; i++) {dist[i] = 999;parent[i] = -1;visit[i] = false;}for (m = G.s[x].firstnode; m; m = m->next) {  //只找相邻点dist[m->number] = 1;parent[m->number] = x;}//先将起始点收入集合dist[x] = 0;visit[x] = true;while (findmindist(dist, visit) != -1) {v = findmindist(dist, visit);visit[v] = true;for (w = 0; w < G.sum; w++) {if (visit[w] == false && next(v, w) == true) {if (dist[w] > dist[v] + 1) {dist[w] = dist[v] + 1;parent[w] = v;}}}}
}void printmy(int x, int y, int parent[], int dist[]) {putimage(0, 0, &sucai7);string sresult="";                        //用来可视化int x1 = 0;int y1 = 100;int s = 0;int nums[100];int k = 0;int t = y;//这里犯了个致命的错误,如果两个点之间没有相连就会一直死循环if (dist[t] == 999) {settextcolor(WHITE);settextstyle(50, 0, L"黑体");outtextxy(900, 500, L"不在已知路线中,请联系管理员");cout << "不在已知路线中,请联系管理员" << endl;return;}while (parent[y] != x) {nums[k++] = parent[y];y = parent[y];}sresult = sresult.append("从");sresult = sresult.append(G.s[x].name);sresult = sresult.append("到");sresult = sresult.append(G.s[t].name);sresult = sresult.append("需要");sresult = sresult.append(to_string(dist[t]));sresult = sresult.append("站");cout << "从" << G.s[x].name << "到" << G.s[t].name << "需要" << dist[t] << "站" << endl;settextcolor(WHITE);settextstyle(50, 0, L"黑体");outtextxy(0, 50, multiByteToWideChar(sresult));sresult = "";//这里我们做一个小小的改进,也就是我们希望得到啊具体的路线信息//也就是说两个以上相邻的站点才有可能是一路//我们开辟一个新的数组来储存线路过程//重要算法int result[100];result[0] = x;for (int i = 1; i <= k; i++) {result[i] = nums[k - i];}result[k + 1] = t;int towards = 0;//表示方向,0表示向后查找,1表示向前查找int i = 0;while (1) {for (int j = 0; j < v.size(); j++) {             //线路容器int m = 0;   //开关int num = 0;//先要找到首个站点的所在容器int n = 0;//这里注意如果从开头查找就要进行第二次判断while (n < v[j].sum) {if (v[j].zhandian[n] == result[i] && i == 0) {     //先找到首个站点在容器中的位置ni++;num++;m = 2;break;}//就是上一次路线断了之后查找第一个站点位置,他又可能往回坐车,所以或着也需要判断else if (v[j].zhandian[n - 1] == result[i - 1] && n > 0 && v[j].zhandian[n] == result[i] && i > 0) {cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;sresult = sresult.append("乘坐");sresult = sresult.append(v[j].name);sresult = sresult.append(" ");sresult = sresult.append(G.s[result[i - 1]].name);sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);i++;num = num + 2;m = 1;break;}else if (v[j].zhandian[n - 1] == result[i] && n > 0 && v[j].zhandian[n] == result[i - 1] && i > 0) {cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;sresult = sresult.append("乘坐");sresult = sresult.append(v[j].name);sresult = sresult.append(" ");sresult = sresult.append(G.s[result[i - 1]].name);sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);i++;n--;num = num + 2;m = 1;break;}else {n++;}}if (n >= v[j].sum)continue;//这里之所有不n++是因为可能是往前找的if (result[i] == v[j].zhandian[n + 1] && m == 1) {n++;cout << "->" << G.s[result[i]].name;sresult=sresult.append("->");sresult=sresult.append(G.s[result[i]].name);towards = 0;num++;i++;n++;while (n < v[j].sum) {if (result[i] == v[j].zhandian[n]) {cout << "->" << G.s[result[i]].name;sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);n++;i++;num++;}else {break;}}}else if (result[i] == v[j].zhandian[n + 1] && m == 2) {n++;cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;sresult = sresult.append("乘坐");sresult = sresult.append(v[j].name);sresult = sresult.append(" ");sresult = sresult.append(G.s[result[i - 1]].name);sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);towards = 0;num++;i++;n++;while (n < v[j].sum) {if (result[i] == v[j].zhandian[n]) {cout << "->" << G.s[result[i]].name;sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);n++;i++;num++;}else {break;}}}//向前查找if (v[j].zhandian[n - 1] == result[i] && m == 1) {n--;cout << "->" << G.s[result[i]].name;sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);towards = 1;num++;i++;n--;while (n < v[j].sum && n >= 0) {if (result[i] == v[j].zhandian[n]) {cout << "->" << G.s[result[i]].name;sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);n--;i++;num++;}else {break;}}}else if (v[j].zhandian[n - 1] == result[i] && m == 2) {n--;cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;sresult = sresult.append("乘坐");sresult = sresult.append(v[j].name);sresult = sresult.append(" ");sresult = sresult.append(G.s[result[i - 1]].name);sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);towards = 1;num++;i++;n--;while (n < v[j].sum && n >= 0) {if (result[i] == v[j].zhandian[n]) {cout << "->" << G.s[result[i]].name;sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);n--;i++;num++;}else {break;}}}if (num >= 2) {settextcolor(WHITE);settextstyle(30, 0, L"黑体");cout << endl;if (10 + y1 * s >= 950) {x1 = x1 + 600;s = 0;}outtextxy(x1,200+ y1 * s, multiByteToWideChar(sresult));s++;sresult = "";                  //一次路线结束清空break;}}if (i > k + 1) {break;}}
}//添加路线
//这边有一个点我们需要知道已有的站点中是否存在该站点
//如果有就不做操作
//如果没有就需要写入新的站点
//对于我们所使用的算法即使重复添加路线也并不会产生影响
//在读取的同时还要进行追加写入
int Insert_Road() {putimage(0, 0, &sucai4);settextcolor(BLACK);EasyTextBox roadname;EasyTextBox roadnumber;EasyTextBox roadguocheng1;EasyTextBox roadguocheng2;EasyTextBox roadguocheng3;EasyTextBox roadguocheng4;roadname.Create(1230, 284, 1580,360 ,10);						// 创建用户名文本框控件roadnumber.Create(1620, 284,1740, 360, 10);						// 创建用户名文本框控件roadguocheng1.Create(1230, 500, 1778, 577, 50);						// 创建用户名文本框控件roadguocheng2.Create(1230, 577, 1778, 654, 50);						// 创建用户名文本框控件roadguocheng3.Create(1230, 654, 1778, 731, 50);						// 创建用户名文本框控件roadguocheng4.Create(1230, 731, 1778, 808, 50);						// 创建用户名文本框控件Road_Sum++;fstream  fin;     //写入格式fin.open("路线信息.txt", ios::out | ios::app);Road r;int n;int flag = 0;        //用于开关,也就是判断是否有该站点,1表示有cout << "请输入您添加路线的名称" << endl;ExMessage msg;while (true){msg = getmessage(EX_MOUSE);			// 获取消息输入if (msg.message == WM_LBUTTONDOWN){// 判断控件if (roadname.Check(msg.x, msg.y))	roadname.OnMessage();// 判断控件if (roadnumber.Check(msg.x, msg.y))	roadnumber.OnMessage();// 判断控件if (roadguocheng1.Check(msg.x, msg.y))	roadguocheng1.OnMessage();if (roadguocheng2.Check(msg.x, msg.y))	roadguocheng2.OnMessage();if (roadguocheng3.Check(msg.x, msg.y))	roadguocheng3.OnMessage();if (roadguocheng4.Check(msg.x, msg.y))	roadguocheng4.OnMessage();}if (kbhit()) {char ch = getch();if (ch == 13)break;}}string Road_name;string Road_number;string Road_guocheng1;string Road_guocheng2;string Road_guocheng3;string Road_guocheng4;Road_name = wideCharToMultiByte(roadname.Text());Road_number= wideCharToMultiByte(roadnumber.Text());Road_guocheng1= wideCharToMultiByte(roadguocheng1.Text());Road_guocheng2 = wideCharToMultiByte(roadguocheng2.Text());Road_guocheng3 = wideCharToMultiByte(roadguocheng3.Text());Road_guocheng4 = wideCharToMultiByte(roadguocheng4.Text());//这边最好加一个判断,如果添加的线路已经存在就直接退出for (int i = 0; i < v.size(); i++) {if (Road_name.compare(v[i].name) == 0) {cout << "线路已存在,请重新输入" << endl;return 0;}}fin << Road_name << endl;r.name = Road_name;cout << "请输入您添加路线的站点数量" << endl;//如果输入不是数字会报错n = stoi(Road_number);r.sum = n;cout << "请逐个输入您的路线" << endl;string station_name[100];int i = 0;for (int j = 0; j< Road_guocheng1.length(); j++) {if (Road_guocheng1[j] == ' ' && Road_guocheng1[j + 1] != ' ') {                 //以空格作为分隔符i++;continue;}else if (Road_guocheng1[i] == ' ' && Road_guocheng1[i + 1] == ' ')continue;station_name[i] += Road_guocheng1[j];                      //将分割好的字符串放到K数组里}i++;for (int j = 0; j < Road_guocheng2.length(); j++) {if (Road_guocheng2[j] == ' ' && Road_guocheng2[j + 1] != ' ') {                 //以空格作为分隔符i++;continue;}else if (Road_guocheng2[i] == ' ' && Road_guocheng2[i + 1] == ' ')continue;station_name[i] += Road_guocheng2[j];                      //将分割好的字符串放到K数组里}i++;for (int j = 0; j < Road_guocheng3.length(); j++) {if (Road_guocheng3[j] == ' ' && Road_guocheng3[j + 1] != ' ') {                 //以空格作为分隔符i++;continue;}else if (Road_guocheng3[i] == ' ' && Road_guocheng3[i + 1] == ' ')continue;station_name[i] += Road_guocheng3[j];                      //将分割好的字符串放到K数组里}i++;for (int j = 0; j < Road_guocheng4.length(); j++) {if (Road_guocheng4[j] == ' ' && Road_guocheng4[j + 1] != ' ') {                 //以空格作为分隔符i++;continue;}else if (Road_guocheng4[i] == ' ' && Road_guocheng4[i + 1] == ' ')continue;station_name[i] += Road_guocheng4[j];                      //将分割好的字符串放到K数组里}for (int j = 0; j < n; j++) {fin << station_name[j] << endl;}//结束后不要忘记加endfin << "end" << endl;fin.close();fin.open("站点信息.txt", ios::out | ios::app);for (int i = 0; i < n; i++) {flag = 0;for (int j = 0; j < G.sum; j++) {if (station_name[i].compare(G.s[j].name) == 0) {flag = 1;break;}}if (flag == 0) {fin << G.sum << " " << station_name[i] << endl;G.s[G.sum].number = G.sum;G.s[G.sum].name = station_name[i];m.insert(pair<string, int>(station_name[i], G.sum++));}}fin.close();for (int i = 0; i < n; i++) {map<string, int>::iterator iter;iter = m.find(station_name[i]);r.zhandian[i] = iter->second;}v.push_back(r);//接下来直接插入线路for (int j = 0; j < r.sum - 1; j++) {int a1 = r.zhandian[j];int a2 = r.zhandian[j + 1];situation* s1 = (situation*)malloc(sizeof(situation));situation* s2 = (situation*)malloc(sizeof(situation));s2->number = G.s[a2].number;s1->number = G.s[a1].number;s2->next = G.s[a1].firstnode;G.s[a1].firstnode = s2;s1->next = G.s[a2].firstnode;G.s[a2].firstnode = s1;}cout << "添加成功" << endl;return 1;
}//首先我们要明确,在删除路线的时候站点不需要发生改变
//我们这里的需求是删除某一条线路,线路名字已知
//这里就体现出了重复建立节点的好处,即使删除了两点之间的节点也不会导致其他有相同节点的路线发生变化
int Delete_Road() {putimage(0, 0, &sucai5);settextcolor(BLACK);EasyTextBox roadname;roadname.Create(1250, 360, 1755, 455, 10);						// 创建用户名文本框控件ExMessage msg;while (true){msg = getmessage(EX_MOUSE);			// 获取消息输入if (msg.message == WM_LBUTTONDOWN){// 判断控件if (roadname.Check(msg.x, msg.y))	roadname.OnMessage();}if (kbhit()) {char ch = getch();if (ch == 13)break;}}int flag = 0;          //开关,用来判断是否存在该路线Road r;Road_Sum--;cout << "请输入你要删除的路线" << endl;string tmp;tmp= wideCharToMultiByte(roadname.Text());auto it = v.begin();while (it != v.end()) {if (it->name.compare(tmp) == 0) {r = *it;v.erase(it);     //删除这个路径flag = 1;break;}it++;}if (flag == 0) {cout << "抱歉,不存在该路线" << endl;return 0;}//这里先写从图中删除数据,明确是两两之间进行删除//要从某站点中开始找,即遍历链表找到与之对应的站点,进行节点删除//我们要知道一个节点的前驱节点else {for (int i = 0; i < r.sum - 1; i++) {int a1 = r.zhandian[i];int a2 = r.zhandian[i + 1];situation* tmp1 = G.s[a1].firstnode;    //前驱节点situation* tmp2 = tmp1->next;           //后驱节点//开头就等于if (tmp1->number == a2) {G.s[a1].firstnode = tmp1->next;free(tmp1);//释放空间continue;}else {while (tmp2 != NULL) {if (tmp2->number == a2) {tmp1->next = tmp2->next;free(tmp2);break;}else {tmp1 = tmp1->next;tmp2 = tmp2->next;}}}//此时第一个节点已经完成删除//开始第二个节点tmp1 = G.s[a2].firstnode;tmp2 = tmp1->next;if (tmp1->number == a1) {G.s[a2].firstnode = tmp1->next;free(tmp1);//释放空间continue;}else {while (tmp2 != NULL) {if (tmp2->number == a2) {tmp1->next = tmp2->next;free(tmp2);break;}else {tmp1 = tmp1->next;tmp2 = tmp2->next;}}}}//接下来我们需要进行文件的删除//原理很简单,就是我们找出需要删除的路线,将其他路线全部读取到tmp中间文件中,然后再拷贝回来即可string s;ifstream fin;fstream Fin;Fin.open("tmp.txt", ios::out | ios::app);fin.open("路线信息.txt", ios::in);while (getline(fin, s, '\n')) {if (s.compare(tmp) != 0) {Fin << s << endl;}else {while (getline(fin, s, '\n')) {if (s.compare("end") == 0)break;}}}Fin.close();fin.close();ofstream file_writer1("路线信息.txt", ios_base::out);//清空文本文件Fin.open("路线信息.txt", ios::out | ios::app);fin.open("tmp.txt", ios::in);//重新写入while (getline(fin, s, '\n')) {Fin << s << endl;}//清空tmp文件ofstream file_writer2("tmp.txt", ios_base::out);//清空文本文件Fin.close();fin.close();cout << "删除成功" << endl;return 1;}
}int check_in(char zhanghao[], char mima[]) {char check_zhanghao[60] = { 'Z','H','Z','S','S','B' };char check_mima[60] = { 'Y','H','W','Z','N','B' };if (strcmp(zhanghao, check_zhanghao) == 0 && strcmp(mima, check_mima) == 0) {cout << "登录成功" << endl;return 1;}else {cout << "账号或密码错误" << endl;return 0;}
}void choice1() {while (1) {putimage(0, 0, &sucai3);char choice;choice = getch();if (choice == '1') {int flag = Insert_Road();settextcolor(WHITE);settextstyle(30, 0, L"黑体");if (flag == 0)outtextxy(1250, 400, L"线路已存在,请重新输入");elseouttextxy(1250, 825, L"添加成功");}else if (choice == '2') {int flag = Delete_Road();settextcolor(WHITE);settextstyle(30, 0, L"黑体");if (flag == 0)outtextxy(1250, 825, L"抱歉,不存在该路线");elseouttextxy(1250, 825, L"删除成功");}else if (choice == '3') {break;}outtextxy(1250, 900, L"是否退出管理员权限,按Y确认,按N继续");cout << "是否退出管理员权限,按Y确认" << endl;char ys;int yss = 0;while (1) {ys = getch();if (ys == 'Y') {yss = 0;break;}else if (ys == 'N') {yss = 1;break;}}if (yss == 0) {break;}elsecontinue;}
}void choice2() {    //也就是用户权限string start;string end;while (1) {putimage(0, 0, &sucai6);cout << "请选择您所需要的服务" << endl;cout << "1.查询最短路线" << endl;cout << "2.查看所有站点" << endl;char ch = getch();if (ch == '1') {putimage(0, 0, &sucai8);settextcolor(BLACK);settextstyle(40, 0, L"黑体");EasyTextBox startstation;EasyTextBox endstation;startstation.Create(1310, 450, 1750, 540, 10);						// 创建用户名文本框控件endstation.Create(1310, 640, 1750, 730, 10);						// 创建用户名文本框控件cout << "请输入您的起始点和终点,例如:博物馆 桔园" << endl;ExMessage msg;while (true){msg = getmessage(EX_MOUSE);			// 获取消息输入if (msg.message == WM_LBUTTONDOWN){// 判断控件if (startstation.Check(msg.x, msg.y))	startstation.OnMessage();if (endstation.Check(msg.x, msg.y))	endstation.OnMessage();}if (kbhit()) {char ch = getch();if (ch == 13)break;}}start= wideCharToMultiByte(startstation.Text());end= wideCharToMultiByte(endstation.Text());map<string, int>::iterator iter1;map<string, int>::iterator iter2;iter1 = m.find(start);iter2 = m.find(end);if (iter1 != m.end() && iter2 != m.end()) {Dijkstra(iter1->second, dist, parent, Visit);bofang4();printmy(iter1->second, iter2->second, parent, dist);}else {settextcolor(WHITE);settextstyle(30, 0, L"黑体");outtextxy(1310, 830, L"输入的地址有问题,请重新输入");cout << "输入的地址有问题,请重新输入" << endl;}}else if (ch == '2') {bofang4();putimage(0, 0, &sucai7);settextcolor(WHITE);settextstyle(30, 0, L"黑体");int x = 200;int y = 30;int k = 0;for (int i = 0; i < G.sum; i++) {if (10 + y * k >= 950) {x = x + 300;k = 0;}outtextxy(x, 10 + y * k, multiByteToWideChar(G.s[i].name));k++;cout << G.s[i].name << endl;}}else {settextcolor(WHITE);settextstyle(30, 0, L"黑体");outtextxy(1265, 800, L"选择有误,请重新选择");}settextcolor(WHITE);settextstyle(30, 0, L"黑体");outtextxy(1265, 900, L"是否退出? 按Y确认,按N则继续查询");cout << "是否退出? 按Y确认,按任意键则继续查询" << endl;char ys;int yss = 0;while (1) {ys = getch();if (ys == 'Y') {yss = 0;break;}else if (ys == 'N') {yss = 1;break;}}if (yss == 0) {break;}elsecontinue;}
}//主函数
int main() {init();          //初始化Creat_Graph();   //创建图init_background();  //初始化图片bofang1();int f = 0;      //总声音开关,就是经过一次循环后这个会变成1,然后再while循环开始时播放音乐while (1) {if(f==1)mciSendString(L"play 素材库/背景1.mp3 repeat", 0, 0, 0);char choice = bofang2();if (choice == '2') {choice2();f = 1;}else if (choice == '1') {if (YS == 1)choice1();f = 1;}}
}

 

 接下来讲解一下大概的算法思想(不想看的可以跳过):

1.首先是文件的读取和写入

  文件读取这一方法在这个程序中占有不可取代的地位,首先需要知道c++中的文件读取方式,ifstream是读取流,fstream是输入流,具体的使用方法可以去网上搜索,这边介绍两种最简单的使用方法

以下是写入方式,该写入方法会覆盖原本的文件,相当于从头开始写入,并不建议

fstream  fin;     //写入格式
fin.open("路线信息.txt", ios::out);

该方法属于写入方法的追加写入,也是比较常用的方法 

fstream  fin;     //写入格式
fin.open("路线信息.txt", ios::out | ios::app);

 以下是读取方式,比较简单

ifstream ReadFile;
ReadFile.open("路线信息.txt", ios::in);//ios::in 表示以只读的方式读取文件

2.初始化图

  文章中的初始化函数包括初始化数据以及创建图

//初始化
void init() {G.sum = CountLines();Road_Sum = CountRoads();ifstream  fin;fin.open("站点信息.txt", ios::in);if (!fin){std::cerr << "cannot open the file";}for (int i = 0; i < G.sum; i++) {int a;string s;fin >> a >> s;m.insert(pair<string, int>(s, a));G.s[i].number = a;G.s[i].name = s;}fin.close();
}//创建图
void Creat_Graph() {ifstream  fin;fin.open("路线信息.txt", ios::in);if (!fin){std::cerr << "cannot open the file";}for (int i = 0; i < Road_Sum; i++) {int sum = 0;Road r;r.sum = 0;fin >> r.name;string t;fin >> t;while (t.compare("end") != 0) {map<string, int>::iterator iter;iter = m.find(t);if (iter != m.end()) {r.zhandian[sum++] = iter->second;fin >> t;}}r.sum = sum;//结束之后直接开始点之间相连for (int j = 0; j < r.sum - 1; j++) {int a1 = r.zhandian[j];int a2 = r.zhandian[j + 1];situation* s1 = (situation*)malloc(sizeof(situation));situation* s2 = (situation*)malloc(sizeof(situation));s2->number = G.s[a2].number;s1->number = G.s[a1].number;s2->next = G.s[a1].firstnode;G.s[a1].firstnode = s2;s1->next = G.s[a2].firstnode;G.s[a2].firstnode = s1;}v.push_back(r); //将路线添加到容器里}fin.close();
}

其实这些学过数据结构的同学应该都是会的,但是这边我要讲一下为什么这个项目中采取了邻接表的形式而不是邻接矩阵,我认为主要有以下两点考虑:

1.公交管理系统中的数据大概率是一个稀疏矩阵,如果使用矩阵的形式储存就会浪费很多空间。

2.在后续的添加和删除路线时,矩阵会直接切断两个站点之间的连接,可能导致其他路线无法正常进行,而使用邻接表时就会重复添加某个站点(这个站点存在于多条路线中),注意,重复添加站点并不会导致程序出现问题,相反,在删除站点时,由于站点多次创建,删除一个依旧会有该站点存在,就不会导致bug出现,这也是一个核心思想。

3.Dijkstra算法

整个算法由三个部分组成

bool next(int x, int y) {   //为了判断这两个点是否相连situation* p;for (p = G.s[x].firstnode; p; p = p->next) {if (p->number == y)return true;}return false;
}int findmindist(int dist[], bool visit[]) {    //找最小的路径int minv, v;int mindist = 999;for (v = 0; v < G.sum; v++) {if (visit[v] == false && dist[v] < mindist) {mindist = dist[v];minv = v;}}if (mindist < 999) {return minv;}elsereturn -1;//表示不存在
}void Dijkstra(int x, int dist[], int parent[], bool visit[])    //Dijkstra算法,传入源顶点
{int v, w;situation* m;//初始化for (int i = 0; i < G.sum; i++) {dist[i] = 999;parent[i] = -1;visit[i] = false;}for (m = G.s[x].firstnode; m; m = m->next) {  //只找相邻点dist[m->number] = 1;parent[m->number] = x;}//先将起始点收入集合dist[x] = 0;visit[x] = true;while (findmindist(dist, visit) != -1) {v = findmindist(dist, visit);visit[v] = true;for (w = 0; w < G.sum; w++) {if (visit[w] == false && next(v, w) == true) {if (dist[w] > dist[v] + 1) {dist[w] = dist[v] + 1;parent[w] = v;}}}}
}

以上是基于邻接表的迪杰斯特拉算法,函数结束后,dist中储存着源节点到每个节点的最短距离,这边不建议采用弗洛伊德算法大家懂得都懂。

4.找出需要经过的路线并打印

这部分算法是一个难点,你需要沿着某一条路线一直走,然后在某个点下车换成另一辆车,详细代码有点屎山,注释都写了有需要的小伙伴可以研究一下(忽略可视化部分)

void printmy(int x, int y, int parent[], int dist[]) {putimage(0, 0, &sucai7);string sresult="";                        //用来可视化int x1 = 0;int y1 = 100;int s = 0;int nums[100];int k = 0;int t = y;//这里犯了个致命的错误,如果两个点之间没有相连就会一直死循环if (dist[t] == 999) {settextcolor(WHITE);settextstyle(50, 0, L"黑体");outtextxy(900, 500, L"不在已知路线中,请联系管理员");cout << "不在已知路线中,请联系管理员" << endl;return;}while (parent[y] != x) {nums[k++] = parent[y];y = parent[y];}sresult = sresult.append("从");sresult = sresult.append(G.s[x].name);sresult = sresult.append("到");sresult = sresult.append(G.s[t].name);sresult = sresult.append("需要");sresult = sresult.append(to_string(dist[t]));sresult = sresult.append("站");cout << "从" << G.s[x].name << "到" << G.s[t].name << "需要" << dist[t] << "站" << endl;settextcolor(WHITE);settextstyle(50, 0, L"黑体");outtextxy(0, 50, multiByteToWideChar(sresult));sresult = "";//这里我们做一个小小的改进,也就是我们希望得到啊具体的路线信息//也就是说两个以上相邻的站点才有可能是一路//我们开辟一个新的数组来储存线路过程//重要算法int result[100];result[0] = x;for (int i = 1; i <= k; i++) {result[i] = nums[k - i];}result[k + 1] = t;int towards = 0;//表示方向,0表示向后查找,1表示向前查找int i = 0;while (1) {for (int j = 0; j < v.size(); j++) {             //线路容器int m = 0;   //开关int num = 0;//先要找到首个站点的所在容器int n = 0;//这里注意如果从开头查找就要进行第二次判断while (n < v[j].sum) {if (v[j].zhandian[n] == result[i] && i == 0) {     //先找到首个站点在容器中的位置ni++;num++;m = 2;break;}//就是上一次路线断了之后查找第一个站点位置,他又可能往回坐车,所以或着也需要判断else if (v[j].zhandian[n - 1] == result[i - 1] && n > 0 && v[j].zhandian[n] == result[i] && i > 0) {cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;sresult = sresult.append("乘坐");sresult = sresult.append(v[j].name);sresult = sresult.append(" ");sresult = sresult.append(G.s[result[i - 1]].name);sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);i++;num = num + 2;m = 1;break;}else if (v[j].zhandian[n - 1] == result[i] && n > 0 && v[j].zhandian[n] == result[i - 1] && i > 0) {cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;sresult = sresult.append("乘坐");sresult = sresult.append(v[j].name);sresult = sresult.append(" ");sresult = sresult.append(G.s[result[i - 1]].name);sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);i++;n--;num = num + 2;m = 1;break;}else {n++;}}if (n >= v[j].sum)continue;//这里之所有不n++是因为可能是往前找的if (result[i] == v[j].zhandian[n + 1] && m == 1) {n++;cout << "->" << G.s[result[i]].name;sresult=sresult.append("->");sresult=sresult.append(G.s[result[i]].name);towards = 0;num++;i++;n++;while (n < v[j].sum) {if (result[i] == v[j].zhandian[n]) {cout << "->" << G.s[result[i]].name;sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);n++;i++;num++;}else {break;}}}else if (result[i] == v[j].zhandian[n + 1] && m == 2) {n++;cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;sresult = sresult.append("乘坐");sresult = sresult.append(v[j].name);sresult = sresult.append(" ");sresult = sresult.append(G.s[result[i - 1]].name);sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);towards = 0;num++;i++;n++;while (n < v[j].sum) {if (result[i] == v[j].zhandian[n]) {cout << "->" << G.s[result[i]].name;sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);n++;i++;num++;}else {break;}}}//向前查找if (v[j].zhandian[n - 1] == result[i] && m == 1) {n--;cout << "->" << G.s[result[i]].name;sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);towards = 1;num++;i++;n--;while (n < v[j].sum && n >= 0) {if (result[i] == v[j].zhandian[n]) {cout << "->" << G.s[result[i]].name;sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);n--;i++;num++;}else {break;}}}else if (v[j].zhandian[n - 1] == result[i] && m == 2) {n--;cout << "乘坐" << v[j].name << " " << G.s[result[i - 1]].name << "->" << G.s[result[i]].name;sresult = sresult.append("乘坐");sresult = sresult.append(v[j].name);sresult = sresult.append(" ");sresult = sresult.append(G.s[result[i - 1]].name);sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);towards = 1;num++;i++;n--;while (n < v[j].sum && n >= 0) {if (result[i] == v[j].zhandian[n]) {cout << "->" << G.s[result[i]].name;sresult = sresult.append("->");sresult = sresult.append(G.s[result[i]].name);n--;i++;num++;}else {break;}}}if (num >= 2) {settextcolor(WHITE);settextstyle(30, 0, L"黑体");cout << endl;if (10 + y1 * s >= 950) {x1 = x1 + 600;s = 0;}outtextxy(x1,200+ y1 * s, multiByteToWideChar(sresult));s++;sresult = "";                  //一次路线结束清空break;}}if (i > k + 1) {break;}}
}

5.添加删除路线

这个部分是最简单的没有什么特别好说的,就是找到对应的站点,然后删除或者添加即可。

//添加路线
//这边有一个点我们需要知道已有的站点中是否存在该站点
//如果有就不做操作
//如果没有就需要写入新的站点
//对于我们所使用的算法即使重复添加路线也并不会产生影响
//在读取的同时还要进行追加写入
int Insert_Road() {putimage(0, 0, &sucai4);settextcolor(BLACK);EasyTextBox roadname;EasyTextBox roadnumber;EasyTextBox roadguocheng1;EasyTextBox roadguocheng2;EasyTextBox roadguocheng3;EasyTextBox roadguocheng4;roadname.Create(1230, 284, 1580,360 ,10);						// 创建用户名文本框控件roadnumber.Create(1620, 284,1740, 360, 10);						// 创建用户名文本框控件roadguocheng1.Create(1230, 500, 1778, 577, 50);						// 创建用户名文本框控件roadguocheng2.Create(1230, 577, 1778, 654, 50);						// 创建用户名文本框控件roadguocheng3.Create(1230, 654, 1778, 731, 50);						// 创建用户名文本框控件roadguocheng4.Create(1230, 731, 1778, 808, 50);						// 创建用户名文本框控件Road_Sum++;fstream  fin;     //写入格式fin.open("路线信息.txt", ios::out | ios::app);Road r;int n;int flag = 0;        //用于开关,也就是判断是否有该站点,1表示有cout << "请输入您添加路线的名称" << endl;ExMessage msg;while (true){msg = getmessage(EX_MOUSE);			// 获取消息输入if (msg.message == WM_LBUTTONDOWN){// 判断控件if (roadname.Check(msg.x, msg.y))	roadname.OnMessage();// 判断控件if (roadnumber.Check(msg.x, msg.y))	roadnumber.OnMessage();// 判断控件if (roadguocheng1.Check(msg.x, msg.y))	roadguocheng1.OnMessage();if (roadguocheng2.Check(msg.x, msg.y))	roadguocheng2.OnMessage();if (roadguocheng3.Check(msg.x, msg.y))	roadguocheng3.OnMessage();if (roadguocheng4.Check(msg.x, msg.y))	roadguocheng4.OnMessage();}if (kbhit()) {char ch = getch();if (ch == 13)break;}}string Road_name;string Road_number;string Road_guocheng1;string Road_guocheng2;string Road_guocheng3;string Road_guocheng4;Road_name = wideCharToMultiByte(roadname.Text());Road_number= wideCharToMultiByte(roadnumber.Text());Road_guocheng1= wideCharToMultiByte(roadguocheng1.Text());Road_guocheng2 = wideCharToMultiByte(roadguocheng2.Text());Road_guocheng3 = wideCharToMultiByte(roadguocheng3.Text());Road_guocheng4 = wideCharToMultiByte(roadguocheng4.Text());//这边最好加一个判断,如果添加的线路已经存在就直接退出for (int i = 0; i < v.size(); i++) {if (Road_name.compare(v[i].name) == 0) {cout << "线路已存在,请重新输入" << endl;return 0;}}fin << Road_name << endl;r.name = Road_name;cout << "请输入您添加路线的站点数量" << endl;//如果输入不是数字会报错n = stoi(Road_number);r.sum = n;cout << "请逐个输入您的路线" << endl;string station_name[100];int i = 0;for (int j = 0; j< Road_guocheng1.length(); j++) {if (Road_guocheng1[j] == ' ' && Road_guocheng1[j + 1] != ' ') {                 //以空格作为分隔符i++;continue;}else if (Road_guocheng1[i] == ' ' && Road_guocheng1[i + 1] == ' ')continue;station_name[i] += Road_guocheng1[j];                      //将分割好的字符串放到K数组里}i++;for (int j = 0; j < Road_guocheng2.length(); j++) {if (Road_guocheng2[j] == ' ' && Road_guocheng2[j + 1] != ' ') {                 //以空格作为分隔符i++;continue;}else if (Road_guocheng2[i] == ' ' && Road_guocheng2[i + 1] == ' ')continue;station_name[i] += Road_guocheng2[j];                      //将分割好的字符串放到K数组里}i++;for (int j = 0; j < Road_guocheng3.length(); j++) {if (Road_guocheng3[j] == ' ' && Road_guocheng3[j + 1] != ' ') {                 //以空格作为分隔符i++;continue;}else if (Road_guocheng3[i] == ' ' && Road_guocheng3[i + 1] == ' ')continue;station_name[i] += Road_guocheng3[j];                      //将分割好的字符串放到K数组里}i++;for (int j = 0; j < Road_guocheng4.length(); j++) {if (Road_guocheng4[j] == ' ' && Road_guocheng4[j + 1] != ' ') {                 //以空格作为分隔符i++;continue;}else if (Road_guocheng4[i] == ' ' && Road_guocheng4[i + 1] == ' ')continue;station_name[i] += Road_guocheng4[j];                      //将分割好的字符串放到K数组里}for (int j = 0; j < n; j++) {fin << station_name[j] << endl;}//结束后不要忘记加endfin << "end" << endl;fin.close();fin.open("站点信息.txt", ios::out | ios::app);for (int i = 0; i < n; i++) {flag = 0;for (int j = 0; j < G.sum; j++) {if (station_name[i].compare(G.s[j].name) == 0) {flag = 1;break;}}if (flag == 0) {fin << G.sum << " " << station_name[i] << endl;G.s[G.sum].number = G.sum;G.s[G.sum].name = station_name[i];m.insert(pair<string, int>(station_name[i], G.sum++));}}fin.close();for (int i = 0; i < n; i++) {map<string, int>::iterator iter;iter = m.find(station_name[i]);r.zhandian[i] = iter->second;}v.push_back(r);//接下来直接插入线路for (int j = 0; j < r.sum - 1; j++) {int a1 = r.zhandian[j];int a2 = r.zhandian[j + 1];situation* s1 = (situation*)malloc(sizeof(situation));situation* s2 = (situation*)malloc(sizeof(situation));s2->number = G.s[a2].number;s1->number = G.s[a1].number;s2->next = G.s[a1].firstnode;G.s[a1].firstnode = s2;s1->next = G.s[a2].firstnode;G.s[a2].firstnode = s1;}cout << "添加成功" << endl;return 1;
}

 

 这边需要特别提示:在我们删除文件中的某一段时,由于没有直接的方法删除,我们需要设置一个中间文本,从原文本中一直读取到需要删除部分的内容然后跳过这段内容,将其他部分的内容都存入这个中间文本中,然后清空原文本,最后将中间文本中的内容再重新输入到原文本中即可,注意中间文本在使用完毕之后需要清空。

//首先我们要明确,在删除路线的时候站点不需要发生改变
//我们这里的需求是删除某一条线路,线路名字已知
//这里就体现出了重复建立节点的好处,即使删除了两点之间的节点也不会导致其他有相同节点的路线发生变化
int Delete_Road() {putimage(0, 0, &sucai5);settextcolor(BLACK);EasyTextBox roadname;roadname.Create(1250, 360, 1755, 455, 10);						// 创建用户名文本框控件ExMessage msg;while (true){msg = getmessage(EX_MOUSE);			// 获取消息输入if (msg.message == WM_LBUTTONDOWN){// 判断控件if (roadname.Check(msg.x, msg.y))	roadname.OnMessage();}if (kbhit()) {char ch = getch();if (ch == 13)break;}}int flag = 0;          //开关,用来判断是否存在该路线Road r;Road_Sum--;cout << "请输入你要删除的路线" << endl;string tmp;tmp= wideCharToMultiByte(roadname.Text());auto it = v.begin();while (it != v.end()) {if (it->name.compare(tmp) == 0) {r = *it;v.erase(it);     //删除这个路径flag = 1;break;}it++;}if (flag == 0) {cout << "抱歉,不存在该路线" << endl;return 0;}//这里先写从图中删除数据,明确是两两之间进行删除//要从某站点中开始找,即遍历链表找到与之对应的站点,进行节点删除//我们要知道一个节点的前驱节点else {for (int i = 0; i < r.sum - 1; i++) {int a1 = r.zhandian[i];int a2 = r.zhandian[i + 1];situation* tmp1 = G.s[a1].firstnode;    //前驱节点situation* tmp2 = tmp1->next;           //后驱节点//开头就等于if (tmp1->number == a2) {G.s[a1].firstnode = tmp1->next;free(tmp1);//释放空间continue;}else {while (tmp2 != NULL) {if (tmp2->number == a2) {tmp1->next = tmp2->next;free(tmp2);break;}else {tmp1 = tmp1->next;tmp2 = tmp2->next;}}}//此时第一个节点已经完成删除//开始第二个节点tmp1 = G.s[a2].firstnode;tmp2 = tmp1->next;if (tmp1->number == a1) {G.s[a2].firstnode = tmp1->next;free(tmp1);//释放空间continue;}else {while (tmp2 != NULL) {if (tmp2->number == a2) {tmp1->next = tmp2->next;free(tmp2);break;}else {tmp1 = tmp1->next;tmp2 = tmp2->next;}}}}//接下来我们需要进行文件的删除//原理很简单,就是我们找出需要删除的路线,将其他路线全部读取到tmp中间文件中,然后再拷贝回来即可string s;ifstream fin;fstream Fin;Fin.open("tmp.txt", ios::out | ios::app);fin.open("路线信息.txt", ios::in);while (getline(fin, s, '\n')) {if (s.compare(tmp) != 0) {Fin << s << endl;}else {while (getline(fin, s, '\n')) {if (s.compare("end") == 0)break;}}}Fin.close();fin.close();ofstream file_writer1("路线信息.txt", ios_base::out);//清空文本文件Fin.open("路线信息.txt", ios::out | ios::app);fin.open("tmp.txt", ios::in);//重新写入while (getline(fin, s, '\n')) {Fin << s << endl;}//清空tmp文件ofstream file_writer2("tmp.txt", ios_base::out);//清空文本文件Fin.close();fin.close();cout << "删除成功" << endl;return 1;}
}

 最后提一点小小的建议:

1.我们需要在学习完一门语言之后进行一定简易项目的实战有利于我们巩固该语言的使用。

2.你在开发一个全新的项目过程中可能会困难重重,就像博主在开发可视化部分的时候经理了3,4次的代码大改(一般来说这是大忌),同时也遇到了许多不可抗拒的困难,由于没有人的帮助,博主只能靠自身的水平和网上搜来的结果一步一步试验,还有就是上一个项目的经验,大家可以看我的上一个项目c语言仿天天酷跑小游戏

 如果代码写的烂不要喷我,额算了想喷就喷吧,希望大家指出我的不足

 整合包(内涵源代码文件以及素材)百度网盘:https://pan.baidu.com/s/1kf5iIhQXSr_1MPSF-BQCRg

 有需要的小伙伴可以加博主微信woyuxiuxian123私聊(白嫖怪勿扰).

 

程序截图:

 

 

 由于图片过多就不一一演示了,里面的登录界面以及抽卡界面全都是动态的,以及配有音效,没有可以来真实博主,跟游戏里面几乎一摸一样。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/422961.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Buttton样式设置background属性失效的问题

最近遇到一个之前没有遇见的问题&#xff0c;就是在添加Button控件的时候发现对其设置background时没有效果&#xff0c;原因是AndroidStudio升级后默认按钮就是主题色&#xff0c;一个比较简单的方法是将Button改为android.widget.Button&#xff0c;对比效果如下&#xff1a;…

提升认知,推荐15个面向开发者的中文播客

前言 对于科技从业者而言&#xff0c;无论是自学成才的程序员&#xff0c;还是行业资深人士&#xff0c;终身学习是很有必要的&#xff0c;尤其是在这样一个技术快速迭代更新的时代。 作为一个摆脱了时间和空间限制的资讯分享平台&#xff0c;播客&#xff08;Podcast&#x…

RK3399平台开发系列讲解(高速设备驱动篇)6.9、SD卡读写流程

🚀返回总目录 文章目录 一、SD卡相关命令介绍1.1、读操作1.2、写操作一、SD卡相关命令介绍 SD卡的读写流程中,需要使用一些特定的命令(CMD)与SD卡进行通信。以下是一些常见的SD卡命令: CMD0(GO_IDLE_STATE): 这是初始化命令,用于将SD卡置于空闲状态。CMD8(SEND_IF_…

数据库中的经纬度数据如何在QGIS中显示

思路&#xff1a;必须先将经纬度数据转换成POINT&#xff0c;MULTILINESTRING等格式才能在QGIS中展示 步骤 1、首先在postgresql数据中建一张包括经纬度数据的表 **注意&#xff1a;**如果是新建数据库&#xff0c;一定要执行如下代码&#xff0c;否则后面的函数ST_GeomFrom…

CTFhub-网站源码

CTFhub-Web-信息泄露-备份文件下载-网站源码 题目信息 解题过程 无脑爆破&#xff08;笑 写个python脚本 import requests #这里的url是你的地址 url "http://challenge-67a05a3755f2610d.sandbox.ctfhub.com:10800/"list1 [web, website, backup, back, www, ww…

VRRP6协议--主备配置

VRRP6概念 VRRP6备份组能够在不改变组网的情况下,采用将多台设备虚拟成一台网关设备,将虚拟交换机设备的IP地址作为用户的默认网关的方式实现下一跳网关的备份。配置VRRP6备份组后,流量通过Master设备转发,当Master设备故障时,迅速选举出新的Master设备继续承担流量转发,…

C++ Primer 6.5 特殊用途语言特性 6.6 函数匹配 知识点+练习题

C Primer6.5 特殊用途语言特性 6.6 函数匹配 默认实参内联函数constexpr函数调试帮助assert预处理宏NDBUG预处理变量 函数匹配练习题 默认实参 string screen(int hz24,int wid80,char c) windowscreen( , ,?)&#xff1b;//错误&#xff01;&#xff0c;只有尾部的实参可以省…

搭建一个简单的Spring Demo

要学习Spring 源码&#xff0c;一个是从Spring GitHub 上去down源码&#xff0c;然后倒入IDEA编译&#xff0c;但这种方法费时费力&#xff0c;如果你不需要对Spring 源码进行修改后&#xff0c;再编译的话&#xff0c;直接搭建一个Spring Demo 的Maven项目&#xff0c;引入Spr…

快速上手的AI工具-文心辅助学习

前言 大家好晚上好&#xff0c;现在AI技术的发展&#xff0c;它已经渗透到我们生活的各个层面。对于普通人来说&#xff0c;理解并有效利用AI技术不仅能增强个人竞争力&#xff0c;还能在日常生活中带来便利。无论是提高工作效率&#xff0c;还是优化日常任务&#xff0c;AI工…

实用技巧——缺失数据的处理

缺失值 处理缺失数据的一般步骤&#xff1a; 识别缺失数据&#xff1b;检查导致数据缺失的原因&#xff1b;删除包含缺失值的实例或用合理的数值代替&#xff08;插补&#xff09;缺失值 缺失数据的分类&#xff1a; &#xff08;1&#xff09;完全随机缺失 若谋变量的缺…

怎么抹掉 Macbook系统 并将它还原为出厂设置

抹掉 Mac 并将它还原为出厂设置 借助“抹掉所有内容和设置”这项功能&#xff0c;你可以快速安全地抹掉所有设置、数据和 App&#xff0c;同时保留当前安装的操作系统。 使用“抹掉所有内容和设置” 这项功能要求装有 macOS Monterey 或更高版本&#xff0c;且使用搭载 Apple 芯…

Python基础第八篇(Python异常处理,模块与包)

文章目录 一、了解异常二、捕获异常&#xff08;1&#xff09;.异常案例代码&#xff08;2&#xff09;.读出结果 三、异常的传递&#xff08;1&#xff09;.异常传递案例代码&#xff08;2&#xff09;.读出结果 四、Python模块&#xff08;1&#xff09;.模块的导入&#xff…