呐,上回书说到写代码过程中遇到不少问题,在这里就一点一点慢慢道来吧。
首先,定义的结构体、全局变量和函数如下:
// Flight.c : 定义应用程序的入口点。 // #include "stdafx.h" //订单 typedef struct OrderForm{ TCHAR IdNum[32]; //订单用户身份证号 int Order_Number; //订单号 int Tickets_Num; //订票数量 TCHAR Flight_Number[16]; //航班号 TCHAR Departure[16]; //出发地 TCHAR Destination[16]; //目的地 TCHAR Date[16]; //出发日期 TCHAR TakeOff_Time[16]; //起飞时间 TCHAR Landing_Time[16]; //降落时间 struct OrderForm *Next; //所有订单链表next struct OrderForm *psgNext; //用户订单链表next }OrderForm; //乘客订单链表 typedef struct PsgOrderLink{ int OrderNum; //订单数目 OrderForm *head; //头结点 OrderForm *tail; //尾结点 }PsgOrderLink; //所有订单列表 typedef struct AllOrderLink{ int AllOrderNum; //所有订单数目 OrderForm *head; //头结点 OrderForm *tail; //尾结点 }AllOrderLink; //乘客 typedef struct Passenger{ TCHAR Name[16]; //姓名 TCHAR IdNum[32]; //身份证号码 TCHAR PassWord[32]; //密码 int TicketNum; //订单数目 PsgOrderLink OrderLink; //用户所有订单 struct Passenger *Next; }Passenger; //乘客链表 typedef struct PsgLinkList{ int PsgNum; //账户数量 Passenger *head; //头结点 Passenger *tail; //尾结点 }PsgLinkList; //航班 typedef struct Flight{ double Fare; //票价 int Seat_Number; //座位数 int Vacant_Seat; //空余座位数 TCHAR Discount[16]; //折扣 TCHAR Flight_Number[16]; //航班号 TCHAR Departure[16]; //出发地 TCHAR Destination[16]; //目的地 TCHAR Date[16]; //出发日期 TCHAR TakeOff_Time[16]; //起飞时间 TCHAR Landing_Time[16]; //降落时间 struct Flight *Next; }Flight; //航班链表 typedef struct FlightLinkList{ Flight *head; //头结点 Flight *tail; //尾结点 int Flight_Number; //航班数目 }FilghtLinkList; // 全局变量: HICON hIcon; HINSTANCE hInst; //当前实例 static TCHAR szBuffer[256]; //缓冲区 static PsgLinkList psglink; //所有账户_链表 static Passenger *passenger; //登陆账户信息 static AllOrderLink allorder; //所有订单_链表 static FilghtLinkList flightlink; //所有航班_链表 //函数声明 BOOL AccountLogIn(HWND); //账户登陆 BOOL AccountRegister(HWND); //注册账户 BOOL ReadFlightData(HWND); //读入航班信息 BOOL ReadAccountData(HWND); //读入账户资料 BOOL ReadAccountOrder(HWND,Passenger*); //读入所有订单、账户订单 BOOL SearchFlight(HWND); //查询航班 BOOL BookTickets(HWND); //订票 BOOL _Book_Tickets(HWND,Flight*,int); //订票 BOOL Recommend(HWND,Flight*,int); //航班建议 BOOL ReturnTickets(HWND); //退票 BOOL EntryFlight(HWND); //录入航班 BOOL ModifyFlight(HWND); //修改航班信息 BOOL PrintFlight(HWND, Flight*); //输出航班信息 BOOL WriteFlightData(HWND); //保存航班信息 BOOL WriteAccountData(HWND); //保存账户资料 BOOL WriteOrderData(HWND); //保存订单信息 BOOL CALLBACK LogInDlgProc(HWND, UINT, WPARAM, LPARAM); //登陆窗口窗口过程 BOOL CALLBACK MainDlgProc(HWND, UINT, WPARAM, LPARAM); //主界面窗口过程 BOOL CALLBACK NameDlgProc(HWND, UINT, WPARAM, LPARAM); //获取新注册用户姓名窗口过程 BOOL CALLBACK FlightNumDlgProc(HWND, UINT, WPARAM, LPARAM); //获取用户输入机票数量窗口过程 BOOL CALLBACK EntryFlightProc(HWND, UINT, WPARAM, LPARAM); //录入航班窗口过程 BOOL CALLBACK ModifyFlightProc(HWND, UINT, WPARAM, LPARAM); //修改航班信息窗口过程主函数如下:
//主函数 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int iCmdShow) { hInst = hInstance; InitCommonControls(); hIcon=LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1)); return DialogBox(hInst, MAKEINTRESOURCE(IDD_LOGINDLG), NULL, (DLGPROC)LogInDlgProc); }//WinMain
登陆界面窗口过程:
//登陆窗口_窗口过程 BOOL CALLBACK LogInDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { //WM_INITDIALOG是当其对话框和子控件全部创建完毕,将要显示内容的时候发送的消息 //因此可以在WM_INITDIALOG消息响应函数中添加对编辑框控件的初始化和修改 case WM_INITDIALOG: { if (hIcon) SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); ReadAccountData(hwndDlg); //登陆对话框初始化时读入账户资料 ReadFlightData(hwndDlg); //登陆对话框初始化时读入航班信息 }//WM_INITDIALOG return TRUE; case WM_CLOSE: { EndDialog(hwndDlg, 0); }//WM_CLOSE return TRUE; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_LOGIN: AccountLogIn(hwndDlg); //登陆 break; case IDC_REGISTER: AccountRegister(hwndDlg); //注册 break; }//switch }//WM_COMMAND return TRUE; }//switch return FALSE; }//LogInDlgProc()
我在对话框初始化时调用了下述函数来加载程序标题栏的图标:
if (hIcon) SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
那么问题就来了,挖掘机...额,不对,应该怎么读入账户资料和航班信息?
我把账户资料和航班信息储存在了.txt文件里,每一行是结构体的一个成员,一个一个读入。
两个读入函数代码如下:
//读入账户信息 BOOL ReadAccountData(HWND hwndDlg){ FILE *fp; passenger = (Passenger *)malloc(sizeof(Passenger)); //为登录账户分配内存 if (!passenger){ MessageBox(hwndDlg, TEXT("内存申请错误!"), TEXT("错误"), MB_OK | MB_ICONERROR); EndDialog(hwndDlg, 0); return FALSE; }//if psglink.head = NULL; psglink.tail = NULL; psglink.PsgNum = 0; if ((fp = fopen(".\\AccountData.txt", "r+")) == NULL){ //打开文件 MessageBox(hwndDlg, TEXT("账户文件读入错误!"), TEXT("错误"), MB_OK | MB_ICONERROR); EndDialog(hwndDlg, 0); }//if while (!feof(fp)){ Passenger *p = (Passenger *)malloc(sizeof(Passenger)); if (!p){ MessageBox(hwndDlg, TEXT("内存申请错误!"), TEXT("错误"), MB_OK | MB_ICONERROR); EndDialog(hwndDlg, 0); return FALSE; }//if //读入账户姓名、身份证号、密码 if (fscanf(fp, "%s%s%s", p->Name, p->IdNum, p->PassWord) == EOF){ free(p); break; } p->Next = NULL; if (psglink.head == NULL) //读入第一个账户信息时,头、尾结点均指向p psglink.head = p; else psglink.tail->Next = p; //否则,尾结点Next指向p psglink.tail = p; //尾结点指向p psglink.PsgNum++; //已注册账户个数 }//while fclose(fp); //关闭文件 return TRUE; }//ReadAccountData(HWND) //读入航班信息 BOOL ReadFlightData(HWND hwndDlg){ int flag = 0; FILE *fp; flightlink.Flight_Number = 0; flightlink.head = NULL; flightlink.tail = NULL; if ((fp = fopen(".\\FlightData.txt", "r")) == NULL){ //打开文件 MessageBox(hwndDlg, TEXT("账户文件读入错误!"), TEXT("错误"), MB_OK | MB_ICONERROR); EndDialog(hwndDlg, 0); }//if while (!feof(fp)){ Flight *p = (Flight *)malloc(sizeof(Flight)); if (!p){ MessageBox(hwndDlg, TEXT("内存申请错误!"), TEXT("错误"), MB_OK | MB_ICONERROR); EndDialog(hwndDlg, 0); return FALSE; }//if //读入航班信息 if (fscanf(fp, "%s%lf%s%d%d%s%s%s%s%s", p->Flight_Number, &p->Fare, p->Discount, &p->Seat_Number, &p->Vacant_Seat, p->Departure, p->Destination, p->Date, p->TakeOff_Time, p->Landing_Time) == EOF) { free(p); break; } p->Next = NULL; if (flightlink.head == NULL) //添加至链表 flightlink.head = p; else flightlink.tail->Next = p; flightlink.tail = p; flightlink.Flight_Number++; }//while fclose(fp); //关闭文件 return TRUE; }//ReadFlightData()
#define _CRT_SECURE_NO_WARNINGS
或者是在项目->属性->c/c++->预处理器->预处理器定义,后面加上_CRT_SECURE_NO_WARNINGS就行了。
结下来又遇到问题了:程序退出把数据写入txt文件时每行末尾都会有一个\n,读入数据的时候用的是feof()函数,而feof()返回的其实是"最后一次读操作的内容",与数据库中的eof()不同,eof()读取的是当前指针位置。因此在读到文件末尾的时候,程序会多执行一次循环,为了防止这种情况,我采取的是检查fscanf()返回值的方法,在此以读入账户资料时为例:
//读入账户姓名、身份证号、密码 if (fscanf(fp, "%s%s%s", p->Name, p->IdNum, p->PassWord) == EOF){ <span style="white-space:pre"> </span>free(p); break; }下面继续贴函数代码:
//登陆用户验证 BOOL AccountLogIn(HWND hwndDlg){ int flag = 0; //flag!=0则该用户已注册,flag==0则该用户尚未注册 Passenger *p; GetDlgItemText(hwndDlg, IDC_IDEDIT, passenger->IdNum, 256); //获取用户输入ID GetDlgItemText(hwndDlg, IDC_PSWEDIT, passenger->PassWord, 256); //获取用户输入密码 p = psglink.head; while (p){ //在用户链表查找登陆用户ID if (!lstrcmp(passenger->IdNum, p->IdNum)){ flag++; //该用户已注册 if (!lstrcmp(passenger->PassWord, p->PassWord)){ //密码匹配,登陆成功 lstrcpy(passenger->Name, p->Name); MessageBox(hwndDlg, TEXT("登陆成功,单击确定进入程序主界面"), TEXT("登陆成功"), MB_OK | MB_ICONINFORMATION); break; }//if else{ //密码错误,退出循环 MessageBox(hwndDlg, TEXT("密码错误,请重新输入"), TEXT("密码错误"), MB_OK | MB_ICONERROR); return FALSE; }//else }//if p = p->Next; }//while if (!flag){ //用户尚未注册 if (lstrlen(passenger->PassWord)==0) MessageBox(hwndDlg, TEXT("请输入密码!"), TEXT("信息"), MB_OK | MB_ICONINFORMATION); else MessageBox(hwndDlg, TEXT("该账户尚未注册,请先注册"), TEXT("信息"), MB_OK | MB_ICONINFORMATION); }//if else{ //关闭登陆界面,弹出主界面 EndDialog(hwndDlg, TRUE); if (ReadAccountOrder(hwndDlg, passenger)) //读取用户订单 DialogBox(hInst, MAKEINTRESOURCE(IDD_MAINDLG), NULL, (DLGPROC)MainDlgProc); }//else return TRUE; }//AccountLogIn(HWND)
//用户注册 BOOL AccountRegister(HWND hwndDlg){ GetDlgItemText(hwndDlg, IDC_IDEDIT, passenger->IdNum, 256); //获取用户输入ID GetDlgItemText(hwndDlg, IDC_PSWEDIT, passenger->PassWord, 256); //获取用户输入密码 if (lstrlen(passenger->PassWord) == 0){ MessageBox(hwndDlg, TEXT("请输入注册用户密码!"), TEXT("信息"), MB_OK | MB_ICONINFORMATION); return FALSE; } DialogBox(hInst, MAKEINTRESOURCE(IDD_NAMEDLG), NULL, (DLGPROC)NameDlgProc); //获取新注册用户姓名 if (lstrlen(passenger->Name) == 0) return FALSE; passenger->Next = NULL; if (psglink.head == NULL) //该注册账户为第一个账户时,头、尾结点均指向passenger psglink.head = passenger; else psglink.tail->Next = passenger; //将新注册账户资料添加至账户链表 psglink.tail = passenger; //链表尾指针指向链表尾 psglink.PsgNum++; //注册用户数目加1 passenger->TicketNum = 0; passenger->OrderLink.head = NULL; //乘客订单链表初始化 passenger->OrderLink.tail = NULL; passenger->OrderLink.OrderNum = 0; MessageBox(hwndDlg, TEXT("注册成功!请单击确定进入主界面"), TEXT("注册成功"), MB_OK | MB_ICONINFORMATION); EndDialog(hwndDlg, TRUE); //关闭登陆界面 DialogBox(hInst, MAKEINTRESOURCE(IDD_MAINDLG), NULL, (DLGPROC)MainDlgProc); //弹出主界面 return TRUE; }//AccountRegister(HWND)
//获取新注册用户姓名_窗口过程 BOOL CALLBACK NameDlgProc(HWND hNameDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){ switch (uMsg) { case WM_INITDIALOG: { if (hIcon) SendMessage(hNameDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); } return TRUE; case WM_CLOSE: { wsprintf(passenger->IdNum, TEXT("\0")); EndDialog(hNameDlg, 0); }//WM_CLOSE return TRUE; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_GETNAMEOK: { GetDlgItemText(hNameDlg, IDC_GETNAME, passenger->Name, 16); EndDialog(hNameDlg, TRUE); break; }//GETNAME case IDC_GETNAMECANCEL: { wsprintf(passenger->IdNum, TEXT("\0")); EndDialog(hNameDlg, TRUE); break; }//GETNAMECANCEL }//stitch }//WM_COMMAND return TRUE; }//stitch return FALSE; }//NameDlgProc()
//读取订单信息 BOOL ReadAccountOrder(HWND hwndDlg, Passenger *passenger){ FILE *fp; allorder.AllOrderNum = 0; //订单链表初始化 allorder.head = NULL; allorder.tail = NULL; passenger->TicketNum = 0; //乘客订单链表初始化 passenger->OrderLink.head = NULL; passenger->OrderLink.tail = NULL; passenger->OrderLink.OrderNum = 0; if ((fp = fopen(".\\OrderForm.txt", "r")) == NULL){ //打开存储订单信息文件 MessageBox(hwndDlg, TEXT("订单文件读入错误!"), TEXT("错误"), MB_OK | MB_ICONERROR); return FALSE; } while (!feof(fp)){ OrderForm *p = (OrderForm *)malloc(sizeof(OrderForm)); if (!p){ MessageBox(hwndDlg, TEXT("内存申请错误!"), TEXT("错误"), MB_OK | MB_ICONERROR); EndDialog(hwndDlg, 0); return FALSE; }//if if (fscanf(fp, "%s%d%d%s%s%s%s%s%s", p->IdNum, //读取订单信息 &p->Order_Number, &p->Tickets_Num, p->Flight_Number, p->Departure, p->Destination, p->Date, p->TakeOff_Time, p->Landing_Time) == EOF) { free(p); break; } p->Next = NULL; p->psgNext = NULL; if (allorder.head == NULL){ //将结点添至订单链表末尾 allorder.head = p; } else allorder.tail->Next = p; allorder.tail = p; allorder.AllOrderNum++; //订单数目增加 if (!lstrcmp(p->IdNum, passenger->IdNum)){ //若该订单为当前登录账户订单,则添至用户订单链表末尾 if (passenger->OrderLink.head == NULL){ //将结点添至用户订单链表末尾 passenger->OrderLink.head = p; } else passenger->OrderLink.tail->psgNext = p; passenger->OrderLink.tail = p; passenger->TicketNum += p->Tickets_Num; passenger->OrderLink.OrderNum++; //订单数目增加 }//if }//while fclose(fp); return TRUE; }//ReadAccountOrder()