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++中的虚拟继承的一些总结(虚拟继承,覆盖,派生,隐藏)

1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如下: class A class B1:public virtual A; class B2:pu

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

C++的模板(八):子系统

平常所见的大部分模板代码,模板所传的参数类型,到了模板里面,或实例化为对象,或嵌入模板内部结构中,或在模板内又派生了子类。不管怎样,最终他们在模板内,直接或间接,都实例化成对象了。 但这不是唯一的用法。试想一下。如果在模板内限制调用参数类型的构造函数会发生什么?参数类的对象在模板内无法构造。他们只能从模板的成员函数传入。模板不保存这些对象或者只保存他们的指针。因为构造函数被分离,这些指针在模板外

可视化实训复习篇章

前言: 今天,我们来学习seaborn库可视化,当然,这个建立在Matplotlib的基础上,话不多说,进入今天的正题吧!当然,这个是《python数据分析与应用》书中,大家有需求的可以参考这本书。 知识点: Matplotlib中有两套接口分别是pyplot和pyylab,即绘图时候主要导入的是Matplotlib库下的两个子模块(两个py文件)matplotlib.pyplot和matp

C++工程编译链接错误汇总VisualStudio

目录 一些小的知识点 make工具 可以使用windows下的事件查看器崩溃的地方 dumpbin工具查看dll是32位还是64位的 _MSC_VER .cc 和.cpp 【VC++目录中的包含目录】 vs 【C/C++常规中的附加包含目录】——头文件所在目录如何怎么添加,添加了以后搜索头文件就会到这些个路径下搜索了 include<> 和 include"" WinMain 和

C/C++的编译和链接过程

目录 从源文件生成可执行文件(书中第2章) 1.Preprocessing预处理——预处理器cpp 2.Compilation编译——编译器cll ps:vs中优化选项设置 3.Assembly汇编——汇编器as ps:vs中汇编输出文件设置 4.Linking链接——链接器ld 符号 模块,库 链接过程——链接器 链接过程 1.简单链接的例子 2.链接过程 3.地址和

C++必修:模版的入门到实践

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C++学习 贝蒂的主页:Betty’s blog 1. 泛型编程 首先让我们来思考一个问题,如何实现一个交换函数? void swap(int& x, int& y){int tmp = x;x = y;y = tmp;} 相信大家很快就能写出上面这段代码,但是如果要求这个交换函数支持字符型

在线装修管理系统的设计

管理员账户功能包括:系统首页,个人中心,管理员管理,装修队管理,用户管理,装修管理,基础数据管理,论坛管理 前台账户功能包括:系统首页,个人中心,公告信息,论坛,装修,装修队 开发系统:Windows 架构模式:B/S JDK版本:Java JDK1.8 开发工具:IDEA(推荐) 数据库版本: mysql5.7 数据库可视化工具: navicat 服务器:SpringBoot自带 ap

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

C++入门01

1、.h和.cpp 源文件 (.cpp)源文件是C++程序的实际实现代码文件,其中包含了具体的函数和类的定义、实现以及其他相关的代码。主要特点如下:实现代码: 源文件中包含了函数、类的具体实现代码,用于实现程序的功能。编译单元: 源文件通常是一个编译单元,即单独编译的基本单位。每个源文件都会经过编译器的处理,生成对应的目标文件。包含头文件: 源文件可以通过#include指令引入头文件,以使