日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

数据结构课程设计——机票售卖系统(C++)

發布時間:2025/3/21 c/c++ 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构课程设计——机票售卖系统(C++) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引言

這學期最后的數據結構課程設計需要我們完成一個簡單的小程序,我選擇了一個機票售賣系統,實現了一些基本的功能;因為時間給的比較短,又趕在復習周補課,所以并沒有什么突出的地方,我就在這里聊聊我的代碼實現和可以進一步改進的地方;

注:該程序并沒有使用C++的面向對象部分內容 而是使用面向過程編程,主要使用了C++的一些容器和函數;

實現過程

基本功能

功能很簡單,就是以下五種:
1,購票
2,退票
3,顯示用戶信息
4,查詢用戶信息
5,查看航班信息

這里因為是售票系統,所以對航班的增加刪除等操作并沒有添加,當然如果你想加也很容易實現;

雖然功能很簡單,但是對于輸入信息的判斷需要保持嚴謹,所以如何制定判斷規則也是要考慮的;

數據結構

首先考慮了用什么數據結構存儲用戶信息和航班信息,因為需要快速檢索信息,并且在這里并沒有使用數據庫而使用最基本的文件操作,所以就考慮使用哈希表來存儲用戶信息和航班信息,并將key值分別設置為身份證后六位航班號;(這里通過map容器來實現哈希表的操作)

結構體

然后就考慮用戶和航班的結構體,代碼如下:

// 客戶信息結構體 struct Person {string name; // 姓名string age; // 年齡string gender; // 性別string idNum; // 身份證號string myAirNum; // 用戶航班號 };// 航班信息結構體 struct Airplane {string airNum; // 航班號string startPoint; // 起點string destination; // 目的地string tickets; // 票數string ticketPrice; // 票價string departureTime; // 起飛時間 };

很簡單,也沒什么可以說的,可能有人會發現:為什么全部變量都用的是string類型?主要考慮到初始化時要把文件內容讀取到內存中,而從文件種讀取的都是字符串,所以就直接全部定義成了字符串;
當然這樣不一定是最好的辦法,我也想到了定義一套轉換體系,但是時間比較緊所以就沒有過多在這里糾結;如果你有更好的方法也歡迎交流一下!

文件

因為在這我并沒有用到數據庫而使用文件,所以并不需要考慮表結構的設計,但是還是要設計一下基本的文件格式的;

首先文件選用兩個.txt文件,一個是userInfo存放用戶信息,一個是airplane存放航班信息;

接下來需要考慮如何存放,因為初始化要把文件內容讀入內存中,所以我定義每個用戶信息(航班信息)占一行,每個屬性間以一個空格劃分,這樣讀取文件時一次就只讀一行,讀入后以空格為基礎劃分字符串;

文件內容如圖:

函數

下面就可以想想需要什么函數了,我把函數分為兩大類:基本功能函數工具函數

函數注釋已經很詳細了,除非特殊的我會詳細說一下,其他就不多說了;

一、基本功能函數

函數聲明:

void initInfo(); // 初始化信息 void UI(); // UI界面 void showUserInfo(); // 顯示輸出用戶信息 void showAirplaneInfo(); // 顯示輸出航班信息 void sellTickets(); // 售票 void selectUserInfo(string idNum); // 查詢用戶信息 void refundTickets(string idNum); // 退票

UI界面

打印出來操作界面以及操作的匯總;

/// <summary> /// UI界面 /// </summary> void UI() {while (true) {// 打印界面cout << "************歡迎使用xxxx航空購票系統**********" << endl;cout << "************** 1, 購 票 **************" << endl;cout << "************** 2, 退 票 **************" << endl;cout << "************** 3,查看購票信息 **************" << endl;cout << "************** 4,查找用戶信息 **************" << endl;cout << "************** 5,查看航班信息 **************" << endl;cout << "************** 6, 退 出 **************" << endl;int choose; // 選項cin >> choose; // 輸入選擇switch (choose) {case 1 : sellTickets(); // 售票break;case 2: {string id;while (true) {cout << "請輸入退票人的身份證號碼:";cin >> id;// 判斷輸入身份證號是否合法if (idNumIsLegal(id)) {break;}cout << "身份證號不合法,請重新輸入!" << endl;}refundTickets(id); // 退票break;}case 3 :showUserInfo(); // 查看購票信息break;case 4: {string id;while (true) {cout << "請輸入查詢用戶的身份證號碼:";cin >> id;// 判斷輸入身份證號是否合法if (idNumIsLegal(id)) {break;}cout << "身份證號不合法,請重新輸入!" << endl;}selectUserInfo(id); // 查看查詢用戶的信息break;}case 5 : showAirplaneInfo(); // 查看航班信息break;case 6 : cout << "歡迎下次使用!" << endl;exit(-1); // 退出}system("pause");system("cls");} }

初始化信息

主要就是為了開始把文件中的內容初始化到內存中,方便之后的操作;

/// <summary> /// 初始化信息 /// </summary> void initInfo() {ifstream ifs01;// 對航班信息進行初始化ifs01.open("airplane.txt", ios::in); // 打開文件// 如果文件打開失敗if (!ifs01.is_open()) {cout << "文件打開失敗!\a" << endl;exit(-1);}// 如果航班信息不為空string ainfo; // 臨時存放每一個航班信息// 按行讀取客戶信息while (getline(ifs01, ainfo)) {vector<string> separateInfo; // 存放分離的信息separateInfo = split(ainfo, " "); // 分離航班信息Airplane airplane; // 存放臨時航班信息airplane.airNum = separateInfo[0]; // 存放航班號airplane.startPoint = separateInfo[1]; // 存放起點airplane.destination = separateInfo[2]; // 存放目的地airplane.tickets = separateInfo[3]; // 存放票數airplane.ticketPrice = separateInfo[4]; // 存放票價airplane.departureTime = separateInfo[5]; // 存放起飛時間// 航班信息存儲string key = separateInfo[0]; // 獲取key值,即航班號airplaneInfo.insert(pair<string, Airplane>(key, airplane)); // 將航班信息存入map}ifs01.close(); // 對用戶信息進行初始化ifstream ifs02;ifs02.open("userInfo.txt", ios::in); // 打開文件// 如果文件打開失敗if (!ifs02.is_open()) {cout << "文件打開失敗!\a" << endl;exit(-1);}// 如果用戶信息不為空string info; // 臨時存放每一個客戶信息// 按行讀取客戶信息while (getline(ifs02, info)) {vector<string> separateInfo; // 存放分離的信息separateInfo = split(info, " "); // 分離客戶信息Person person; // 存放臨時客戶信息person.name = separateInfo[0]; // 存放姓名person.age = separateInfo[1]; // 存放年齡person.gender = separateInfo[2]; // 存放性別person.idNum = separateInfo[3]; // 存放身份證號person.myAirNum = separateInfo[4]; // 存放航班號// 客戶信息存儲string key = separateInfo[3].substr(13); // 身份證后六位為map索引值keyuserInfo.insert(pair<string, Person>(key, person)); // 將客戶信息存入map}ifs02.close(); }

售票

增加用戶并存入文件;

/// <summary> /// 售票 /// </summary> void sellTickets() {// 1,錄入用戶信息Person person; // 買票客戶// 輸入姓名while (true) {cout << "請輸入姓名:";cin >> person.name;// 判斷姓名是否合法if (nameIsLegal(person.name)) {break;}cout << "姓名不合法,請重新輸入!" << endl;}// 輸入年齡while (true) {cout << "請輸入年齡:";cin >> person.age;// 判斷輸入年齡是否符合常理if (stringToInt(person.age) > 110 || stringToInt(person.age) < 0) {cout << "年齡不合法,請重新輸入!" << endl;}else {break;}}// 輸入性別while (true) {cout << "請輸入性別:";cin >> person.gender;// 判斷輸入性別是否合法if (person.gender == "男" || person.gender == "女") {break;}cout << "性別不合法,請重新輸入!" << endl;}// 輸入身份證號while (true) {cout << "請輸入身份證號:";cin >> person.idNum;// 判斷輸入身份證號是否合法if (idNumIsLegal(person.idNum)) {break;}cout << "身份證號不合法,請重新輸入!" << endl;}// 輸入航班號while (true) {cout << "請輸入航班號:";cin >> person.myAirNum;// 判斷 航班號是否合法 且 航班存在 且 人數未滿if (person.myAirNum[0] == 'x' && person.myAirNum.length() == 4&& airplaneInfo.find(person.myAirNum) != airplaneInfo.end() && stringToInt(airplaneInfo[person.myAirNum].tickets) != 0) {// 購買當前航班,則該航班票數應該減一int tickets = stringToInt(airplaneInfo[person.myAirNum].tickets); // 轉化為整形tickets--; // 票數減一airplaneInfo[person.myAirNum].tickets = to_string(tickets); // 轉化為字符串updateAirplaneFile(); // 更新航班文件 break;}cout << "航班號不合法或者該航班不存在或者航班人數已滿,請重新選擇!" << endl;}// 2,用戶信息存入userInfo中string key = person.idNum.substr(13); // 獲取該用戶對應的key值:身份證號后六位userInfo.insert(pair<string, Person>(key, person)); // 將用戶信息存入map中// 3,新增用戶信息寫入文件操作fstream fs;fs.open("userInfo.txt", ios::out | ios::app); // 打開文件// 寫入文件fs << person.name + " " << person.age + " "<< person.gender + " " << person.idNum + " "<< person.myAirNum + " " << endl;cout << "購票成功!!" << endl;fs.close(); }

顯示輸出用戶信息

/// <summary> /// 顯示輸出用戶信息 /// </summary> void showUserInfo() {// 判斷用戶信息是否為空if (userInfo.empty()) {cout << "用戶信息為空!!" << endl;return;}// 若用戶信息不為空開始遍歷map<string, Person>::iterator it; // 迭代器for (it = userInfo.begin(); it != userInfo.end(); it++) {cout << "姓名:" + it->second.name<< " 年齡:" + it->second.age<< " 性別:" + it->second.gender<< " 身份證號:" + it->second.idNum<< " 航班號:" + it->second.myAirNum << " 起點:" + airplaneInfo[it->second.myAirNum].startPoint<< " 終點:" + airplaneInfo[it->second.myAirNum].destination<< " 票價:" + airplaneInfo[it->second.myAirNum].ticketPrice<< " 起飛時間:" + airplaneInfo[it->second.myAirNum].departureTime<< endl;} }

示輸出航班信息

/// <summary> /// 顯示輸出航班信息 /// </summary> void showAirplaneInfo() {// 如果航班信息為空if (airplaneInfo.empty()) {cout << "航班信息為空!" << endl;return;}// 若航班信息不為空開始遍歷map<string, Airplane>::iterator it; // 迭代器int i = 0;for (it = airplaneInfo.begin(); it != airplaneInfo.end(); it++) {cout << "航班號:" + it->second.airNum<< " 起點:" + it->second.startPoint<< " 終點:" + it->second.destination<< " 票數:" + it->second.tickets<< " 票價:" + it->second.ticketPrice<< " 起飛時間:" + it->second.departureTime<< endl;} }

查詢用戶信息

map直接找就行;

/// <summary> /// 查詢用戶信息 /// </summary> /// <param name="idNum">查詢用戶的身份證號</param> void selectUserInfo(string idNum) {string key = idNum.substr(13); // 獲取key值:身份證號后六位// 判斷用戶信息是否存在 if (userInfo.find(key) == userInfo.end()) {cout <<"該用戶不存在!!" << endl;return ;}Person person = userInfo[key]; // 通過key查找用戶信息// 輸出用戶信息cout << "姓名:" + person.name<< " 年齡:" + person.age<< " 性別:" + person.gender<< " 身份證號:" + person.idNum<< " 航班號:" + person.myAirNum << " 起點:" + airplaneInfo[person.myAirNum].startPoint<< " 終點:" + airplaneInfo[person.myAirNum].destination<< " 票價:" + airplaneInfo[person.myAirNum].ticketPrice<< " 起飛時間:" + airplaneInfo[person.myAirNum].departureTime<< endl; }

退票

退票不僅要在內存中刪除用戶信息,文件中也要刪除;但是文件操作并沒有直接刪除的功能,所以當內存中的用戶信息刪除后,把所有文件信息清空,然后再重寫把內存中的用戶信息寫入文件就可以了;

/// <summary> /// 退票 /// </summary> /// <param name="idNum">退票用戶身份證號</param> void refundTickets(string idNum) {// 判斷該用戶是否存在string key = idNum.substr(13); // 如果不存在if (userInfo.find(key) == userInfo.end()) {cout << "用戶信息不存在!" << endl;return;}// 如果存在,該航班票數需要增加一int tickets = stringToInt(airplaneInfo[userInfo[key].myAirNum].tickets); // 字符串轉整型tickets++; // 票數加一airplaneInfo[userInfo[key].myAirNum].tickets = to_string(tickets); // 整型轉字符串updateAirplaneFile(); // 更新航班文件 userInfo.erase(key); // 從用戶信息中刪除// 從文件中刪除該用戶信息clearFile("userInfo.txt"); // 清空文件// 重新寫入文件fstream fs;fs.open("userInfo.txt", ios::out | ios::app); // 打開文件// 遍歷重新寫入用戶信息for (auto i : userInfo) {fs << i.second.name + " "<< i.second.age + " "<< i.second.gender + " "<< i.second.idNum + " "<< i.second.myAirNum << endl;}fs.close();cout << "退票成功!!" << endl; }

二、工具函數

函數聲明:

vector<string> split(const string& str, const string& delim); // 字符串分割函數 bool idNumIsLegal(string idNum); // 判斷身份證號是否合法 bool nameIsLegal(string name); // 判斷姓名是否合法 int stringToInt(string str); // 將string類型轉化為int類型 void clearFile(const string fileName); // 清空文件內容 void updateAirplaneFile(); // 更新航班文件信息

字符串分割函數

因為C++沒有split函數,但是可以通過C語言的strtok函數實現相應的功能,于是我自行封裝了一個字符串分割函數;

/// <summary> /// 字符串分割函數 /// </summary> /// <param name="str">需要的分割的字符串</param> /// <param name="delim">分割標準</param> /// <returns>字符串數組</returns> vector<string> split(const string& str, const string& delim) {vector<string> res; // 存放分割字符串的數組if ("" == str) return res; // 字符串為空則無法分割// 先將要切割的字符串從string類型轉換為char*類型 char* strs = new char[str.length() + 1]; strcpy(strs, str.c_str());char* d = new char[delim.length() + 1];strcpy(d, delim.c_str()); // 將delim轉化為char*賦給dchar* p = strtok(strs, d); // 首次調用指向分解字符串while (p) {string s = p; // 分割得到的字符串轉換為string類型 res.push_back(s); // 存入結果數組 p = strtok(NULL, d); // 之后每一次分割,分解字符串處為NULL}return res; }

判斷身份證號是否合法

其實判斷身份證號合法只需要把身份證號分為幾部分分別判斷就可以了;
但是這樣有點麻煩,可以選擇更簡單的正則表達式實現判斷,正好C++也支持正則表達式,所以我就網上找到了身份證號的正則表達式直接用就好了;

/// <summary> /// 判斷身份證號是否合法 /// </summary> /// <param name="idNum">身份證號</param> /// <returns>合法為true,非法為false</returns> bool idNumIsLegal(string idNum) {// 定義一個 正則表達式 為18位身份證號碼的判定規則regex repPattern("^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$");// 聲明匹配結果變量match_results<string::const_iterator> rerResult;// 進行匹配bool bValid = regex_match(idNum, rerResult, repPattern);if (bValid) {// 匹配成功//cout << "身份證號格式合法!" << endl;return true;}//cout << "身份證號格式不合法!" << endl;return false; }

判斷姓名是否合法

和身份證號判斷一樣,直接使用正則表達式;

/// <summary> /// 判斷姓名是否合法 /// </summary> /// <param name="name">姓名</param> /// <returns>合法為true,非法為false</returns> bool nameIsLegal(string name) {// 定義一個 正則表達式 為18位身份證號碼的判定規則regex repPattern("^[\u4e00-\u9fa5]+(·[\u4e00-\u9fa5]+)*$");// 聲明匹配結果變量match_results<string::const_iterator> rerResult;// 進行匹配bool bValid = regex_match(name, rerResult, repPattern);if (bValid) {// 匹配成功//cout << "姓名格式合法!" << endl;return true;}//cout << "姓名格式不合法!" << endl;return false; }

將string類型轉化為int類型

這個使用的是stoi函數,不了解的可以看一下我的這篇文章:C++中stoi(),atoi() ,to_string()使用技巧

/// <summary> /// 將string類型轉化為int類型 /// </summary> /// <param name="str">需轉化的字符串</param> /// <returns>該字符串轉化后的整型</returns> int stringToInt(string str) {// 判斷字符串是否為空 if (str.empty()) {cout << "數據出現錯誤,請檢查文件數據!" << endl;return 0;}return stoi(str); // 字符串轉化為整型 }

清空文件內容

/// <summary> /// 清空文件內容 /// </summary> /// <param name="fileName">文件名</param> void clearFile(const string fileName) {fstream file(fileName, ios::out); }

更新航班文件信息

這個就是和更新用戶信息一樣的,就是刪除了重寫,因為多次用到就單獨寫成函數了;

/// <summary> /// 更新航班文件信息 /// </summary> void updateAirplaneFile() {// 從文件中刪除clearFile("airplane.txt"); // 清空文件// 重新寫入文件fstream fs;fs.open("airplane.txt", ios::out | ios::app); // 打開文件// 遍歷重新寫入航班信息for (auto i : airplaneInfo) {fs << i.second.airNum + " "<< i.second.startPoint + " "<< i.second.destination + " "<< i.second.tickets + " "<< i.second.ticketPrice + " "<< i.second.departureTime + " "<< endl;}fs.close(); }

總結

因為這個項目功能實現很簡單,代碼實現起來并沒有那么難;其實一個項目的開始我感覺最不簡單的就是從0到1的過程,當整個框架有了之后其實就沒有什么難度了;

缺點

其實用戶信息的設計還是存在問題的,就是如果獲取了身份證號,那么就可以直接獲取到用戶的性別和年齡了,這兩個變量就是多余的了,所以如果代碼進一步的改進的化,我會優先修改這一點;這也是最初設計存在的問題,結果到最后才想到,這時如果進行修改那么修改內容可就很多了;
對我來說這也是一個教訓,最初構思設計一定要保證嚴謹,這樣才能避免后期大規模的修改;
目前還沒想到其他問題,如果你有其他發現或者有新奇的想法也歡迎來交流;

代碼網盤鏈接

這里就放一個代碼和文件的獲取鏈接,我使用的軟件是vs2019,有些gcc老版本可能無法識別正則表達式,所以你可以自行更新gcc版本或者使用vs2019即以上版本;

百度網盤:
鏈接:https://pan.baidu.com/s/1BkLI-FhpJ05O2sTMUuf6cw
提取碼:xxxx

總結

以上是生活随笔為你收集整理的数据结构课程设计——机票售卖系统(C++)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。