C語(yǔ)言集中上機(jī)實(shí)驗(yàn)報(bào)告 - 車(chē)票管理系統(tǒng)
C語(yǔ)言集中上機(jī)報(bào)告
車(chē)票管理系統(tǒng)
院 系: 計(jì)算機(jī)學(xué)院
班 級(jí): xxx
姓 名: xxx
合 作 者: 無(wú)
指導(dǎo)教師: xxx
2009年 6月 11日
目 錄
一.概述 3
二.總體方案設(shè)計(jì) 3
三.詳細(xì)設(shè)計(jì) 5
四.程序的調(diào)試與運(yùn)行結(jié)果說(shuō)明 8
五.課程設(shè)計(jì)總結(jié) 10
六.后記 10
七.附錄 11
參考文獻(xiàn) 20
一 概述
1. 課程設(shè)計(jì)的目的
熟練掌握C語(yǔ)言的應(yīng)用及特點(diǎn); 掌握函數(shù)聲明、定義和使用的方法;熟練掌握C語(yǔ)言中的對(duì)文件進(jìn)行操作的基本方法;熟練掌握結(jié)構(gòu)體的使用方法;掌握鏈表的基本操作。
2. 課程設(shè)計(jì)的要求
1.要求利用C語(yǔ)言面向過(guò)程的編程思想完成系統(tǒng)設(shè)計(jì);
2.突出C語(yǔ)言的函數(shù)特征,以多個(gè)函數(shù)實(shí)現(xiàn)每一個(gè)子功能;
3.畫(huà)出功能模塊圖;
4.進(jìn)行簡(jiǎn)單的界面設(shè)計(jì),能夠?qū)崿F(xiàn)友好交互;
5.具有清晰的程序流程圖和數(shù)據(jù)結(jié)構(gòu)的詳細(xì)定義;
6.源碼格式規(guī)范,要有適當(dāng)?shù)淖⑨專(zhuān)钩绦蛉菀组喿x。
3. 課程設(shè)計(jì)的主要設(shè)計(jì)思想
這次設(shè)計(jì)的車(chē)票管理系統(tǒng),實(shí)現(xiàn)了對(duì)車(chē)票的實(shí)時(shí)管理,功能包括錄入、增加班次信息(信息用文件保存),瀏覽班次信息,查詢(xún)路線(xiàn)以及售票和退票功能。主要使用了鏈表實(shí)現(xiàn)了數(shù)據(jù)的保存和修改,同時(shí)使用文件相關(guān)函數(shù)對(duì)文件進(jìn)行同步。
二 總體方案設(shè)計(jì)
這個(gè)程序比較簡(jiǎn)單,主要分為菜單,瀏覽/查詢(xún),售票/退票,和增加數(shù)據(jù)四個(gè)部分。
采用了鏈表作為存儲(chǔ)的方式,并實(shí)現(xiàn)了結(jié)構(gòu)體與文件的同步修改,已經(jīng)具備了題目要求的所有功能。這個(gè)程序由我個(gè)人完成。
在編程過(guò)程中遇到的關(guān)鍵問(wèn)題有系統(tǒng)時(shí)間的獲取與顯示,從文件中讀取鏈表,處理每天班次的人數(shù)刷新問(wèn)題,以及如何提高代碼的復(fù)用率等。
程序整體框圖如下:
啟動(dòng)程序
文件是否存在
5增加新班次記錄
結(jié)構(gòu)體鏈表
文件是否為空
否
是
是
修改
3查找班次
4退出系統(tǒng)
數(shù)據(jù)文件
banci.dat
修改
菜單
1-5
否
關(guān)閉文件
將文件讀入結(jié)構(gòu)體鏈表
更新班次日期等
讀取
寫(xiě)入
修改
1顯示班次
讀取
調(diào)用
2售票 / 退票
調(diào)用
判斷是否能夠售票/退票(根據(jù)時(shí)間,已訂票人數(shù))
是
修改
三 詳細(xì)設(shè)計(jì)
程序結(jié)構(gòu)以及各函數(shù)的具體功能
main.c:
void main();
程序入口,對(duì)文件進(jìn)行打開(kāi)和初始化后進(jìn)入菜單
void menu(struct banci *head,struct banci *nw,FILE *data,unsigned int serial);
顯示菜單,從主函數(shù)中得到各種信息,通過(guò)switch語(yǔ)句調(diào)用其他功能
operate.c:
void update(struct banci *ut,FILE *data);
更新結(jié)構(gòu)體中的各個(gè)數(shù)據(jù),主要對(duì)日期進(jìn)行處理,當(dāng)班次更新后重新寫(xiě)入日期并將已訂票人數(shù)清零
void displaybanci(struct banci *head, unsigned short mode);
作為整個(gè)程序中唯一的數(shù)據(jù)顯示函數(shù),顯示班次信息,有3個(gè)顯示模式
struct banci * addbanci(struct banci * tail, FILE *data,unsigned serial);
增加一條新的班次信息到鏈表末尾,并寫(xiě)入文件
void searchbanci(struct banci * head);
查找班次信息,可按班次號(hào)查詢(xún)或按終點(diǎn)站查詢(xún)
void piao(struct banci *head,FILE *data);
實(shí)現(xiàn)售票和退票功能,并自動(dòng)更新文件
operate.h
聲明結(jié)構(gòu)體和各個(gè)自定義函數(shù)
部分函數(shù)的算法分析
1. 對(duì)文件進(jìn)行打開(kāi)和初始化。這個(gè)功能在主函數(shù)main()中完成。有關(guān)代碼如下,具體功能解釋在程序注釋中。
FILE *data;
struct banci *head,*nw;
head=(struct banci*)malloc(sizeof(struct banci));
if((data=fopen("banci.dat","r+"))==NULL)
{
data=fopen("banci.dat","w");
printf("\n\n建立新文件,請(qǐng)至少輸入一條班次記錄\n");
nw=addbanci(head,data,1);
head=nw;
}
else if(fread(head,sizeof(struct banci),1,data)==0) //判斷文件是否為空。fread()返回的是讀取成功的數(shù)據(jù)塊數(shù)目,返回0說(shuō)明讀取失敗。若文件不為空則實(shí)現(xiàn)讀取文件
{
printf("\n\n數(shù)據(jù)文件是空的,請(qǐng)至少輸入一條班次記錄\n");
nw=addbanci(head,data,1);
head=nw;
}
else
{
nw=head;
}
while(1) //把整個(gè)鏈表從文件中讀出來(lái),同時(shí)獲取目前的班次總數(shù)
{
serial++;
//if(nw->next==NULL) break; //這么判斷結(jié)尾時(shí)不可行的,因?yàn)閚w->next已經(jīng)被賦值不可能為空了
nw->next=malloc(sizeof(struct banci));
if((fread(nw->next,sizeof(struct banci),1,data))==0) //逐條讀取。若遇到結(jié)尾,結(jié)束讀取
{
nw->next=NULL;
break;
}
nw=nw->next;
}
2. update() 函數(shù)。使用這個(gè)函數(shù)主要是考慮到系統(tǒng)日期變化后對(duì)班次的售票信息進(jìn)行更新。在程序中每進(jìn)入一次菜單就要執(zhí)行一次update()函數(shù)。當(dāng)結(jié)構(gòu)體中存儲(chǔ)的年月日和系統(tǒng)目前的時(shí)間都相等是則跳過(guò)并不清零已訂票人數(shù),否則對(duì)結(jié)構(gòu)體進(jìn)行更新并清零已訂票人數(shù)。主要代碼如下:
if( nowtime.wYear == ut->fache.wYear &&
nowtime.wMonth == ut->fache.wMonth &&
nowtime.wDay == ut->fache.wDay )
{
fseek(data,sizeof(struct banci),1); //不需要更新,則跳過(guò)這個(gè)結(jié)構(gòu)體,不寫(xiě)文件
}
else
{
ut->fache.wYear=nowtime.wYear;
ut->fache.wMonth=nowtime.wMonth;
ut->fache.wDay=nowtime.wDay;
ut->fache.wDayOfWeek=nowtime.wDayOfWeek;
ut->yidingpiaorenshu=0;
fwrite(ut,sizeof(struct banci),1,data);
}
3. displaybanci() 函數(shù)。通過(guò)顯示模式這一變量,實(shí)現(xiàn)了代碼的重復(fù)利用。當(dāng)顯示整個(gè)鏈表時(shí),循環(huán)顯示所有信息;當(dāng)在查找中僅需要顯示單條記錄時(shí),不再繼續(xù)顯示之后的信息。這是有關(guān)顯示模式的主要代碼:
while(head!=NULL)
{
………………
………………
if (mode==1 || mode==3) //顯示全部記錄時(shí),繼續(xù)顯示
head=head->next;
if (mode==2) //顯示單條記錄時(shí),不再顯示
break;
}
if(mode==1) getch(); //模式2和3不需要暫停,故有這個(gè)判斷
4. 關(guān)于班次的序列號(hào)。這個(gè)程序中班次是固定的,不需要修改或刪除,故班次的序號(hào)又系統(tǒng)自動(dòng)分配,不需要輸入。在程序開(kāi)始初始化文件時(shí),就獲取了目前的班次總數(shù),若再增加班次,則序列號(hào)自動(dòng)加一。具體實(shí)現(xiàn):
(菜單menu()中)case '5': serial++;nw=addbanci(nw,data,serial); break;
(addbanci()中)newnode->serial=serial;
四 程序的調(diào)試與運(yùn)行結(jié)果說(shuō)明
1. 啟動(dòng)程序,提示輸入第一條記錄建立文件
————————————————————————————
建立新文件,請(qǐng)至少輸入一條班次記錄
這是第 1 個(gè)班次
輸入發(fā)車(chē)時(shí)間(小時(shí):分鐘)
————————————————————————————
2. 若文件為空,也提示輸入第一條記錄
————————————————————————————
數(shù)據(jù)文件是空的,請(qǐng)至少輸入一條班次記錄
這是第 1 個(gè)班次
輸入發(fā)車(chē)時(shí)間(小時(shí):分鐘)
————————————————————————————
3. 輸入記錄后進(jìn)入菜單,使用了簡(jiǎn)單的文本菜單界面
————————————————————————————
C題目二十三: 車(chē)票管理系統(tǒng)
今天是 2009 年 6 月 12 日,目前讀取的時(shí)間為 8 時(shí) 37 分
1. 瀏覽班次信息
2. 售票 / 退票
3. 查詢(xún)班次
4. 退出系統(tǒng)
5. 增加班次
————————————————————————————
4. 瀏覽班次信息
————————————————————————————
瀏覽班次
班次 發(fā)車(chē)時(shí)間 起點(diǎn)站 終點(diǎn)站 行車(chē)時(shí)間 額定載量 已定票人數(shù) 是否已發(fā)出
1 8 : 00 aaa bbb 0.5 40 0 已發(fā)出
2 23 : 00 bbb aaa 0.5 40 2
3 12 : 00 rtf bbb 1.5 40 1
4 17 : 30 bbb rtf 1.5 40 0
5 19 : 30 ttt rtf 0.3 40 0
————————————————————————————
6. 查詢(xún)班次信息,按終點(diǎn)站查詢(xún)
————————————————————————————
查詢(xún)班次
1. 按班次號(hào)查詢(xún)
2. 按終點(diǎn)站查詢(xún)
輸入要查詢(xún)的終點(diǎn)站: bbb
瀏覽班次
班次 發(fā)車(chē)時(shí)間 起點(diǎn)站 終點(diǎn)站 行車(chē)時(shí)間 額定載量 已定票人數(shù) 是否已發(fā)出
1 8 : 00 aaa bbb 0.5 40 0 已發(fā)出
瀏覽班次
班次 發(fā)車(chē)時(shí)間 起點(diǎn)站 終點(diǎn)站 行車(chē)時(shí)間 額定載量 已定票人數(shù) 是否已發(fā)出
3 12 : 00 rtf bbb 1.5 40 1
————————————————————————————
7. 售票、退票,以售票為例
————————————————————————————
瀏覽班次
班次 發(fā)車(chē)時(shí)間 起點(diǎn)站 終點(diǎn)站 行車(chē)時(shí)間 額定載量 已定票人數(shù) 是否已發(fā)出
1 8 : 00 aaa bbb 0.5 40 0 已發(fā)出
2 23 : 00 bbb aaa 0.5 40 2
3 12 : 00 rtf bbb 1.5 40 2
4 17 : 30 bbb rtf 1.5 40 0
5 19 : 30 ttt rtf 0.3 40 0
售票 / 退票
1. 售票
2. 退票
輸入要售票的班次號(hào):3
第 3 班次完成一次售票
————————————————————————————
8. 增加班次信息
————————————————————————————
這是第 6 個(gè)班次
輸入發(fā)車(chē)時(shí)間(小時(shí):分鐘)
10:15
輸入起點(diǎn)站
ytr
輸入終點(diǎn)站
aaa
輸入行車(chē)時(shí)間(分鐘)
60
輸入額定載量
32
————————————————————————————
五 課程設(shè)計(jì)總結(jié)
經(jīng)過(guò)測(cè)試,這個(gè)系統(tǒng)已經(jīng)達(dá)到了題目中的全部要求。從功能上講,這個(gè)程序還有不完善的地方,如不能修改或刪除班次信息等。另外,由于追求代碼復(fù)用率的原因,在查詢(xún)班次時(shí)會(huì)顯示出多余的信息,有待進(jìn)一步改進(jìn)。這個(gè)程序有一個(gè)特點(diǎn)就是界面簡(jiǎn)明,但從另一方面講也可以說(shuō)是界面簡(jiǎn)陋,如果可能的話(huà),下一個(gè)版本將采用圖形界面。
在編程過(guò)程中也遇到了一些困難,如系統(tǒng)時(shí)間的獲取與保存,從文件中讀出整個(gè)鏈表等,這些都通過(guò)查詢(xún)資料和詢(xún)問(wèn)同學(xué)得到了解決。另外在程序測(cè)試過(guò)程中發(fā)現(xiàn)了發(fā)車(chē)后訂票人數(shù)不能歸零的問(wèn)題,后來(lái)通過(guò)添加update()函數(shù)得到了解決。
六 后記
經(jīng)過(guò)這次集中上機(jī)實(shí)習(xí),我充分認(rèn)識(shí)到了C語(yǔ)言的用途是非常廣的,功能也非常強(qiáng)大,是學(xué)計(jì)算機(jī)不可缺少的語(yǔ)言。更重要的是,在這次編程中熟悉了編寫(xiě)一個(gè)比較復(fù)雜程序的流程,以及發(fā)現(xiàn)問(wèn)題、解決問(wèn)題的能力,為下一步計(jì)算機(jī)語(yǔ)言的學(xué)習(xí)做了準(zhǔn)備。之前感覺(jué)遙不可及的功能,現(xiàn)在可以實(shí)現(xiàn)了,應(yīng)當(dāng)感謝老師和同學(xué)的熱心幫助,這是我得以及時(shí)完成這個(gè)程序的重要因素。
七 附錄
main.c
#include<stdio.h>
#include<string.h>
#include<windows.h>
#include"operate.h"
unsigned int serial=0; //班次序列號(hào),由系統(tǒng)自動(dòng)生成不能修改
void menu(struct banci *head,struct banci *nw,FILE *data,unsigned int serial);
void main()
{
FILE *data;
struct banci *head,*nw;
//system("mode con cols=80 lines=25"); //定義屏幕寬度
head=(struct banci*)malloc(sizeof(struct banci));
if((data=fopen("banci.dat","r+"))==NULL)
{
data=fopen("banci.dat","w");
printf("\n\n建立新文件,請(qǐng)至少輸入一條班次記錄\n");
nw=addbanci(head,data,1);
head=nw;
}
else if(fread(head,sizeof(struct banci),1,data)==0)
//判斷文件是否為空。fread()返回的是讀取成功的數(shù)據(jù)塊數(shù)目,返回0說(shuō)明讀取失敗。若文件不為空則實(shí)現(xiàn)讀取文件
{
printf("\n\n數(shù)據(jù)文件是空的,請(qǐng)至少輸入一條班次記錄\n");
nw=addbanci(head,data,1);
head=nw;
}
else
{
nw=head;
}
while(1) //把整個(gè)鏈表從文件中讀出來(lái),同時(shí)獲取目前的班次總數(shù)
{
serial++;
//if(nw->next==NULL) break; //這么判斷結(jié)尾時(shí)不可行的,因?yàn)閚w->next已經(jīng)被賦值不可能為空了
nw->next=malloc(sizeof(struct banci));
if((fread(nw->next,sizeof(struct banci),1,data))==0) //逐條讀取。若遇到結(jié)尾,結(jié)束讀取
{
nw->next=NULL;
break;
}
nw=nw->next;
}
menu(head,nw,data,serial);
}
void menu(struct banci *head,struct banci *nw,FILE *data,unsigned int serial)
{
char kinput;
while(1)
{
system("cls");
printf("C題目二十三: 車(chē)票管理系統(tǒng)");
update(head,data);
printf("\n 1. 瀏覽班次信息\n 2. 售票 / 退票\n 3. 查詢(xún)班次\n 4. 退出系統(tǒng)\n 5. 增加班次\n\n");
kinput=getch();
switch(kinput)
{
case '1': displaybanci(head,1);break;
case '2': piao(head,data);break;
case '3': searchbanci(head);break;
case '4': fclose(data);exit(0);
case '5': serial++;nw=addbanci(nw,data,serial); break;
default:;
}
}
}
oprate.c
#include<stdio.h>
#include<string.h>
#include<windows.h>
#include"operate.h"
void update(struct banci *ut,FILE *data) //防止第二天定票人數(shù)不歸零的情況發(fā)生
{
SYSTEMTIME nowtime;
GetLocalTime(&nowtime);
printf("\n今天是 %d 年 %d 月 %d 日,目前讀取的時(shí)間為 %d 時(shí) %d 分\n",
nowtime.wYear,nowtime.wMonth,nowtime.wDay,nowtime.wHour,nowtime.wMinute);
rewind(data);
for(;ut!=NULL;ut=ut->next)
{
if( nowtime.wYear == ut->fache.wYear &&
nowtime.wMonth == ut->fache.wMonth &&
nowtime.wDay == ut->fache.wDay )
{
fseek(data,sizeof(struct banci),1); //不需要更新,則跳過(guò)這個(gè)結(jié)構(gòu)體,不寫(xiě)文件
}
else
{
ut->fache.wYear=nowtime.wYear;
ut->fache.wMonth=nowtime.wMonth;
ut->fache.wDay=nowtime.wDay;
ut->fache.wDayOfWeek=nowtime.wDayOfWeek;
ut->yidingpiaorenshu=0;
fwrite(ut,sizeof(struct banci),1,data);
}
}
}
void displaybanci(struct banci *head, unsigned short mode) //承擔(dān)所有的鏈表顯示,有3個(gè)顯示模式
{
SYSTEMTIME nowtime;
GetLocalTime(&nowtime);
printf("瀏覽班次\n");
printf("班次 發(fā)車(chē)時(shí)間 起點(diǎn)站 終點(diǎn)站 行車(chē)時(shí)間 額定載量 已定票人數(shù) 是否已發(fā)出\n");
while(head!=NULL)
{
printf("%-4d %2d : %02d %-10s%-10s%-10.1f%-9d%-3d ",
head->serial,head->fache.wHour,head->fache.wMinute,head->qidian,head->zhongdian,
(float)head->shijian/60,head->edingzailiang,head->yidingpiaorenshu);
if( nowtime.wHour>head->fache.wHour
|| (head->fache.wHour==nowtime.wHour && nowtime.wMinute>head->fache.wMinute))
{
head->shifouyifachu=1;
printf(" 已發(fā)出\n");
head->yidingpiaorenshu=0;
}
else
{
head->shifouyifachu=0;
printf(" \n");
}
if (mode==1 || mode==3) //顯示全部記錄時(shí),繼續(xù)顯示
head=head->next;
if (mode==2) //顯示單條記錄時(shí),不再顯示
break;
}
if(mode==1) getch(); //模式2和3不需要暫停,故有這個(gè)判斷
}
struct banci * addbanci(struct banci * tail, FILE *data,unsigned serial)
{
struct banci *newnode;
newnode=(struct banci*)malloc(sizeof(struct banci));
GetLocalTime(&newnode->fache); //先調(diào)出系統(tǒng)時(shí)間
printf("\n這是第 %d 個(gè)班次\n",serial);
printf("輸入發(fā)車(chē)時(shí)間(小時(shí):分鐘)\n");
scanf("%d:%d",&newnode->fache.wHour,&newnode->fache.wMinute);
printf("輸入起點(diǎn)站\n");
scanf("%s",newnode->qidian);
printf("輸入終點(diǎn)站\n");
scanf("%s",newnode->zhongdian);
printf("輸入行車(chē)時(shí)間(分鐘)\n");
scanf("%d",&newnode->shijian);
printf("輸入額定載量\n");
scanf("%d",&newnode->edingzailiang);
newnode->yidingpiaorenshu=0; //這兩個(gè)變量由系統(tǒng)自動(dòng)指定無(wú)需輸入
newnode->serial=serial;
tail->next=newnode; //連接鏈表
newnode->next=NULL;
fwrite(newnode,sizeof(struct banci),1,data); //更新文件
fflush(data);
return newnode;
}
void searchbanci(struct banci * head)
{
char sinput;
unsigned sserial;
char szhongdian[8];
unsigned short label=0;
printf("查詢(xún)班次\n\n 1. 按班次號(hào)查詢(xún)\n 2. 按終點(diǎn)站查詢(xún)\n");
sinput=getch();
switch(sinput)
{
case '1':
printf("輸入要查詢(xún)的班次: ");
scanf("%d",&sserial);
for(;head!=NULL;head=head->next)
{
if(head->serial==sserial)
{
label=1;
displaybanci(head,2);
}
}
break;
case '2':
printf("輸入要查詢(xún)的終點(diǎn)站: ");
scanf("%s",szhongdian);
for(;head!=NULL;head=head->next)
{
if(strcmp(szhongdian,head->zhongdian)==0)
{
label=1;
displaybanci(head,2);
}
}
break;
default:;
}
if(label==0)
{
printf("\n沒(méi)有找到或無(wú)法操作\n");
}
getch();
}
void piao(struct banci *node,FILE *data)
{
char pinput;
unsigned int pserial;
unsigned short label=0;
displaybanci(node,3);
printf("售票 / 退票");
printf("\n 1. 售票\n 2. 退票\n");
pinput=getch();
switch(pinput)
{
case '1':
printf("輸入要售票的班次號(hào):");
scanf("%d",&pserial);
for(;node!=NULL;node=node->next)
if(node->serial==pserial)
if(node->shifouyifachu==0 && node->yidingpiaorenshu<node->edingzailiang)
{
node->yidingpiaorenshu++;
fseek(data,(pserial-1)*sizeof(struct banci),0);
fwrite(node,sizeof(struct banci),1,data);
label=1;
printf("\n第 %d 班次完成一次售票\n",pserial);
}
break;
case '2':
printf("輸入要退票的班次號(hào):");
scanf("%d",&pserial);
for(;node!=NULL;node=node->next)
if(node->serial==pserial)
if(node->shifouyifachu==0 && node->yidingpiaorenshu!=0)
{
node->yidingpiaorenshu--;
fseek(data,(pserial-1)*sizeof(struct banci),0);
fwrite(node,sizeof(struct banci),1,data);
label=1;
printf("\n第 %d 班次完成一次退票\n",pserial);
}
break;
default:;
}
if(label==0)
printf("\n沒(méi)有找到,或無(wú)法操作\n");
getch();
}
oprate.h
struct banci
{
unsigned int serial;
SYSTEMTIME fache;
char qidian[10];
char zhongdian[10];
unsigned int shijian; //行車(chē)時(shí)間用分鐘數(shù)保存
unsigned int edingzailiang;
unsigned int yidingpiaorenshu;
unsigned short shifouyifachu;
struct banci *next;
};
void displaybanci(struct banci *head, unsigned short mode);
struct banci * addbanci(struct banci * tail, FILE *data,unsigned serial);
void searchbanci(struct banci * head);
void piao(struct banci *head,FILE *data);
void update(struct banci *ut,FILE *data);
參考文獻(xiàn)
[1] 譚浩強(qiáng),C程序設(shè)計(jì)題解與上機(jī)指導(dǎo)(第二版),北京,清華大學(xué)出版社,2000年9月。
[2] 甘玲 ,解析C程序設(shè)計(jì),北京,清華大學(xué)出版社,2007年3月。