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

2024-01-23 00:04

本文主要是介绍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私聊(白嫖怪勿扰).

 

程序截图:

 

 

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

这篇关于c/c++公交管理系统(星穹轨道可视化EasyX)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/634647

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现