




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
項(xiàng)目四火車(chē)票務(wù)管理系統(tǒng)任務(wù)1錄入火車(chē)時(shí)刻信息任務(wù)2查詢火車(chē)時(shí)刻信息任務(wù)3統(tǒng)計(jì)火車(chē)車(chē)次本項(xiàng)目是要設(shè)計(jì)一個(gè)火車(chē)票務(wù)管理系統(tǒng),實(shí)現(xiàn)火車(chē)時(shí)刻信息的錄入、查詢和統(tǒng)計(jì)。錄入信息包括車(chē)次、日期、起點(diǎn)、終點(diǎn)、開(kāi)車(chē)時(shí)間、到達(dá)時(shí)間、票價(jià)等,查詢信息包括按照車(chē)次查詢、按終點(diǎn)查詢、按起點(diǎn)查詢、按終點(diǎn)和日期查詢等,統(tǒng)計(jì)信息主要包括按起點(diǎn)或終點(diǎn)統(tǒng)計(jì)每日的車(chē)次數(shù)。通過(guò)本項(xiàng)目使學(xué)生掌握結(jié)構(gòu)類(lèi)型的構(gòu)造及使用、文件及預(yù)處理命令。本項(xiàng)目使用結(jié)構(gòu)體類(lèi)型來(lái)實(shí)現(xiàn)“火車(chē)票務(wù)管理系統(tǒng)”中火車(chē)時(shí)刻信息的錄入,利用文件來(lái)查詢“火車(chē)票務(wù)管理系統(tǒng)”中列車(chē)的數(shù)據(jù)。知識(shí)目標(biāo):
掌握結(jié)構(gòu)體類(lèi)型的定義及引用。
掌握使用結(jié)構(gòu)體變量成員運(yùn)算符和指針變量引用成員運(yùn)算符輸入/輸出結(jié)構(gòu)體成員。
掌握文件類(lèi)型指針。
掌握文件的打開(kāi)、關(guān)閉及讀寫(xiě)操作。
掌握預(yù)處理命令的定義及使用方法。能力目標(biāo):
能夠正確定義結(jié)構(gòu)體類(lèi)型和結(jié)構(gòu)體變量。
能將結(jié)構(gòu)體類(lèi)型數(shù)據(jù)定義成數(shù)組。
能將“火車(chē)票務(wù)管理系統(tǒng)”中的列車(chē)信息定義成結(jié)構(gòu)體類(lèi)型。
能實(shí)現(xiàn)對(duì)“火車(chē)票務(wù)管理系統(tǒng)”中的列車(chē)信息進(jìn)行輸入、輸出、查詢、統(tǒng)計(jì)等操作。
能正確使用fopen、fclose函數(shù)對(duì)文件進(jìn)行打開(kāi)與關(guān)閉操作。
能利用fgetc、fputc、fgets、fputs、freed、fwrite、fscanf、fprinf等函數(shù)對(duì)文件進(jìn)行。
任務(wù)1錄入火車(chē)時(shí)刻信息
一、任務(wù)情境
在“火車(chē)票務(wù)管理系統(tǒng)”中,火車(chē)的記錄信息通常采用多個(gè)不同的數(shù)據(jù)類(lèi)型來(lái)存放。例如,火車(chē)車(chē)次為字符型,日期為字符型,起點(diǎn)、終點(diǎn)為字符型,票價(jià)采用整型或?qū)嵭汀S捎跀?shù)據(jù)類(lèi)型多樣,因此不適合采用數(shù)組形式來(lái)存放這樣一組數(shù)據(jù)。為了解決數(shù)據(jù)類(lèi)型多樣化的問(wèn)題,我們引入了新的數(shù)據(jù)類(lèi)型——“結(jié)構(gòu)(structure)”或叫“結(jié)構(gòu)體”,它是一種構(gòu)造類(lèi)型。這種構(gòu)造類(lèi)型是由若干“成員”組成的。每一個(gè)成員類(lèi)型可以是一個(gè)基本數(shù)據(jù)類(lèi)型或者又是一個(gè)構(gòu)造類(lèi)型。因此這種構(gòu)造類(lèi)型也要像基本類(lèi)型一樣在說(shuō)明和使用之前先定義它。二、相關(guān)知識(shí)
1.結(jié)構(gòu)體類(lèi)型的定義
定義結(jié)構(gòu)體類(lèi)型的形式為:
struct結(jié)構(gòu)體名
{
類(lèi)型名成員1;
類(lèi)型名成員2;
……
類(lèi)型名成員n;
};結(jié)構(gòu)體中各個(gè)成員的類(lèi)型既可以是基本類(lèi)型,也可以是構(gòu)造類(lèi)型。成員名的命名應(yīng)符合標(biāo)識(shí)符的命名規(guī)則。例如在“火車(chē)票務(wù)管理系統(tǒng)”結(jié)構(gòu)體類(lèi)型中有如下定義:
structTRAIN
{
chartrainNum[]; //車(chē)次
chardate[]; //日期
charfromPlace[]; //出發(fā)地
chartoPlace[]; //目的地
charinTime[]; //進(jìn)站時(shí)間
charoutTime[]; //發(fā)車(chē)時(shí)間
doubleprice; //列車(chē)票價(jià)
};在這個(gè)結(jié)構(gòu)體的定義中,結(jié)構(gòu)體名為T(mén)RAIN,該結(jié)構(gòu)體由7個(gè)成員組成。其中前6個(gè)成員為字符數(shù)組類(lèi)型,成員名分別為trainNum、date、fromPlace、toPlace、inTime、outTime;第7個(gè)成員為price,浮點(diǎn)型。特別注意的是成員之間的分號(hào)是必不可少的。
我們抽取出結(jié)構(gòu)體類(lèi)型聲明的一般形式是:
struct結(jié)構(gòu)體名
{
成員列表項(xiàng);
};
2.結(jié)構(gòu)體類(lèi)型變量的定義
在定義結(jié)構(gòu)體類(lèi)型后,需要定義結(jié)構(gòu)體類(lèi)型變量,這樣才能在具體的應(yīng)用中引用相關(guān)完整的信息。例如在定義火車(chē)結(jié)構(gòu)體類(lèi)型變量后,這個(gè)變量才能賦予車(chē)次、進(jìn)站時(shí)間、發(fā)車(chē)時(shí)間、日期等信息。
定義結(jié)構(gòu)體類(lèi)型的變量,可以采用以下三種方法:
(1)先聲明結(jié)構(gòu)體類(lèi)型,再定義結(jié)構(gòu)體類(lèi)型變量。前面定義的結(jié)構(gòu)體類(lèi)型是structTRAIN,現(xiàn)在用它來(lái)定義結(jié)構(gòu)體變量。如:
structTRAINtrain1,train2;
在這條語(yǔ)句中,TRAIN是結(jié)構(gòu)體類(lèi)型名;train1,train2是結(jié)構(gòu)體變量名,該變量具備結(jié)構(gòu)體類(lèi)型中的所有成員屬性。(2)在定義結(jié)構(gòu)體類(lèi)型的同時(shí),定義結(jié)構(gòu)體類(lèi)型變量。
structTRAIN
{
chartrainNum[]; //車(chē)次
chardate[]; //日期
charfromPlace[]; //出發(fā)地
chartoPlace[]; //目的地
charinTime[]; //進(jìn)站時(shí)間
charoutTime[]; //發(fā)車(chē)時(shí)間
floatprice; //列車(chē)票價(jià)
}train1,train2;
這種類(lèi)型的定義一般形式為:
struct結(jié)構(gòu)名
{
成員列表項(xiàng);
}變量名列表項(xiàng);
注:使用這種方法定義結(jié)構(gòu)體類(lèi)型時(shí),結(jié)構(gòu)體名可以省略。
3.結(jié)構(gòu)體類(lèi)型變量的賦值及引用
結(jié)構(gòu)體變量和前面講的普通類(lèi)變量的操作不一樣。在使用結(jié)構(gòu)體變量時(shí),往往都是通過(guò)對(duì)其成員進(jìn)行操作來(lái)完成的,其中包括賦值、輸入、輸出、運(yùn)算等。如果想對(duì)結(jié)構(gòu)體變量進(jìn)行整體使用,則只能允許具有相同結(jié)構(gòu)和類(lèi)型的變量對(duì)其進(jìn)行賦值。
1)結(jié)構(gòu)體變量成員的表示方法
表示結(jié)構(gòu)體變量成員的一般形式是:
結(jié)構(gòu)變量名.成員名
例如:
train1.trainnum//即第一趟火車(chē)的車(chē)次
train2.price //即第二趟火車(chē)的票價(jià)
2)結(jié)構(gòu)體變量初始化
【例4.1.1】結(jié)構(gòu)體變量和其他類(lèi)型變量一樣,在定義結(jié)構(gòu)變量時(shí)可以進(jìn)行初始化賦值。3)結(jié)構(gòu)體變量成員賦值
【例4.1.2】
4.結(jié)構(gòu)體數(shù)組的使用
由于結(jié)構(gòu)體變量具有相同的成員屬性,因此為了定義更多的結(jié)構(gòu)體變量,可以使用結(jié)構(gòu)體數(shù)組,如列車(chē)時(shí)刻表、學(xué)生成績(jī)表、公司員工薪金表、企業(yè)商品庫(kù)存表等。
聲明結(jié)構(gòu)體數(shù)組的方法和聲明結(jié)構(gòu)體變量相似。只需要聲明它為數(shù)組類(lèi)型即可,因此在使用和理解上都相對(duì)容易一些。
例如:
structtrain
{
char*trainNum;
char*fromPlace;
char*toPlace;
doubleprice;
}train_info[4];定義了一個(gè)結(jié)構(gòu)體數(shù)組train_info,共有4個(gè)元素,即train_info[0]~train_info[3]。每個(gè)數(shù)組元素都具有structtrain的結(jié)構(gòu)形式。對(duì)結(jié)構(gòu)體數(shù)組可以作初始化賦值。例如:
structtrain
{
char*trainNum;
char*fromPlace;
char*toPlace;
doubleprice;
}train_info[4]={
{"K746","pingdingshan","zhengzhou",32.5},
{"K1212","pingdingshan","zhengzhou",32.5},
{"K910","pingdingshan","zhengzhou",32.5},
{"K184","pingdingshan","zhengzhou",32.5},
};三、任務(wù)實(shí)施
通過(guò)結(jié)構(gòu)體類(lèi)型的相關(guān)知識(shí)的學(xué)習(xí),我們可以定義列車(chē)的結(jié)構(gòu)體類(lèi)型,實(shí)現(xiàn)“火車(chē)票務(wù)管理系統(tǒng)”列車(chē)時(shí)刻表信息的錄入。
1.定義列車(chē)的結(jié)構(gòu)體類(lèi)型
structTRAIN
{
chartrainNum[5];
chardate[10];
charfromPlace[20];
chartoPlace[20];
charinTime[10];
charoutTime[10];
floatprice;
};2.定義能存儲(chǔ)30趟列車(chē)基本信息的結(jié)構(gòu)體數(shù)組
structTRAINtrain[30];
3.錄入列車(chē)時(shí)刻表信息
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
structTRAIN
{
chartrainNum[5];
chardate[10];
charfromPlace[20];
chartoPlace[20];
charinTime[10];
charoutTime[10];
floatprice;
};structTRAINtrain[30];
intmain()
{
charch='Y';
inti=0;
doublemyprice;
printf("火車(chē)票務(wù)管理系統(tǒng)\n");
printf("請(qǐng)錄入火車(chē)時(shí)刻信息:\n");
do
{printf("輸入車(chē)次日期起點(diǎn)終點(diǎn)開(kāi)車(chē)時(shí)間到達(dá)時(shí)間票價(jià)\n>>>");
scanf("%s%s%s%s%s%s%f",train[i].trainNum,train[i].date,train[i].fromPlace,train[i].toPlace,train[i].inTime,train[i].outTime,&train[i].price);
myprice=train[i].price;
i++;
printf("continue(Y/N)(Ntoend)?");
getchar();
scanf("%c",&ch);
}
while(ch!='N');
return0;
}四、知識(shí)拓展
上面講了結(jié)構(gòu)體類(lèi)型的基本使用,以方便大家學(xué)習(xí)和使用。在實(shí)際應(yīng)用和編程過(guò)程中,不僅要用到基本的內(nèi)容。同時(shí)還要使用一些深層次的內(nèi)容。為了能夠讓大家掌握更多的知識(shí),此處要拓展學(xué)習(xí)一些深層次的知識(shí),以作為對(duì)基本內(nèi)容的補(bǔ)充。
【例4.1.3】編程輸出學(xué)生的記錄信息。
1.結(jié)構(gòu)體類(lèi)型的指針變量
在例4.1.3中,采用了結(jié)構(gòu)體類(lèi)型的指針變量(簡(jiǎn)稱(chēng)為結(jié)構(gòu)體指針變量)將學(xué)生信息輸出。什么是結(jié)構(gòu)體指針變量呢?如何來(lái)定義和使用呢?本文將在下面進(jìn)行介紹。
1)結(jié)構(gòu)體指針變量的聲明
一個(gè)指針變量指向一個(gè)結(jié)構(gòu)體變量時(shí),稱(chēng)此指針變量為結(jié)構(gòu)體指針變量。結(jié)構(gòu)體指針變量中的值是所指向的結(jié)構(gòu)體變量的首地址。通過(guò)結(jié)構(gòu)體指針即可訪問(wèn)該結(jié)構(gòu)體變量。
從上述引例中可以描述出結(jié)構(gòu)體指針變量說(shuō)明的一般形式為:
struct結(jié)構(gòu)名*結(jié)構(gòu)體指針變量名
……
structtrain*train1;//聲明結(jié)構(gòu)體指針變量
……
2)結(jié)構(gòu)體指針變量的賦值
在前面介紹過(guò)各類(lèi)指針變量要先賦值后使用,結(jié)構(gòu)體指針變量也必須先賦值后使用。在賦值時(shí),不能把結(jié)構(gòu)體名賦予該指針變量,而是把結(jié)構(gòu)體變量的首地址賦予該指針變量。在上述聲明中,train1是被說(shuō)明為train類(lèi)型的結(jié)構(gòu)體指針變量,如果再聲明一個(gè)結(jié)構(gòu)體變量train2,則:
train1=&train2;
是正確的,而:
train1=&train;
是錯(cuò)誤的。結(jié)構(gòu)體名和結(jié)構(gòu)體變量不能混淆,它們是兩個(gè)不同的概念。結(jié)構(gòu)體名只能表示一個(gè)結(jié)構(gòu)體形式,編譯系統(tǒng)并不對(duì)它分配內(nèi)存空間。只有當(dāng)某變量被說(shuō)明為這種類(lèi)型的結(jié)構(gòu)體時(shí),才對(duì)該變量分配存儲(chǔ)空間。因此上面?&train這種寫(xiě)法是錯(cuò)誤的,不可能去取一個(gè)結(jié)構(gòu)體名的首地址。有了結(jié)構(gòu)體指針變量,就能更方便地訪問(wèn)結(jié)構(gòu)體變量的各個(gè)成員。
3)結(jié)構(gòu)體變量成員的訪問(wèn)
在例4.1.3中,采用了一種方式來(lái)引用結(jié)構(gòu)體指針變量成員,是否還有其他方式呢?下面介紹結(jié)構(gòu)體指針變量成員訪問(wèn)的形式。其訪問(wèn)的一般形式為:
(*結(jié)構(gòu)體指針變量).成員名
或?yàn)椋?/p>
結(jié)構(gòu)體指針變量->成員名
例如:
(*train1).num
或者:
train1->num
特別注意,(*train1)兩側(cè)的括號(hào)必不可少,按照運(yùn)算符的優(yōu)先級(jí)來(lái)看,成員符“?.?”的優(yōu)先級(jí)高于“
*
”。如去掉括號(hào)寫(xiě)作?*train1.num,則等效于?*(train1.num),這樣表示的意義就完全不對(duì)了。【例4.1.4】從運(yùn)行結(jié)果可以看出:
結(jié)構(gòu)體變量.成員名
(*結(jié)構(gòu)體指針變量).成員名
結(jié)構(gòu)體指針變量->成員名
這三種用于表示結(jié)構(gòu)體成員的形式是完全等效的。
2.動(dòng)態(tài)存儲(chǔ)分配
在學(xué)生信息表中,學(xué)生的記錄可能動(dòng)態(tài)增加,因此不能確定所需存儲(chǔ)空間。為了解決這樣的問(wèn)題,在C語(yǔ)言中提供了一些內(nèi)存管理函數(shù),這些內(nèi)存管理函數(shù)可以按照需要?jiǎng)討B(tài)地分配內(nèi)存空間,也可把不再使用的空間回收待用,為有效地利用內(nèi)存資源提供了管理手段。常用的內(nèi)存管理函數(shù)有以下三個(gè):
1)分配內(nèi)存空間函數(shù)(malloc)
該函數(shù)調(diào)用形式為:
(類(lèi)型說(shuō)明符*)malloc(size);
功能:在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)中分配一塊長(zhǎng)度為“size”字節(jié)的連續(xù)區(qū)域。函數(shù)的返回值為該區(qū)域的首地址。
“類(lèi)型說(shuō)明符”表示把該區(qū)域用于存儲(chǔ)何種數(shù)據(jù)類(lèi)型。(類(lèi)型說(shuō)明符*)表示把返回值強(qiáng)制轉(zhuǎn)換為該類(lèi)型指針?!皊ize”是一個(gè)無(wú)符號(hào)數(shù)。
例如:
number=(char*)malloc(1000);
表示分配1000個(gè)字節(jié)的內(nèi)存空間,并強(qiáng)制轉(zhuǎn)換為字符數(shù)組類(lèi)型,函數(shù)的返回值為指向該字符數(shù)組的指針,把該指針賦予指針變量number。
2)分配內(nèi)存空間函數(shù)(calloc)
該函數(shù)調(diào)用形式為:
(類(lèi)型說(shuō)明符*)calloc(m,size);
功能:在內(nèi)存動(dòng)態(tài)存儲(chǔ)區(qū)中分配m塊長(zhǎng)度為“size”字節(jié)的連續(xù)區(qū)域。函數(shù)的返回值為該區(qū)域的首地址。
(類(lèi)型說(shuō)明符*)用于強(qiáng)制類(lèi)型轉(zhuǎn)換。calloc函數(shù)與malloc函數(shù)的區(qū)別僅在于前者一次可以分配m塊區(qū)域。
例如:
Pnum=(struetstu*)calloc(2,sizeof(structstu));
其中的sizeof(structstu)是求stu的結(jié)構(gòu)長(zhǎng)度。因此該語(yǔ)句的意思是:按stu的長(zhǎng)度分配兩塊連續(xù)區(qū)域,強(qiáng)制轉(zhuǎn)換為stu類(lèi)型,并把其首地址賦予指針變量Pnum。
3)釋放內(nèi)存空間函數(shù)(free)
該函數(shù)調(diào)用形式為:
free(void*ptr);
功能:釋放ptr所指向的一塊內(nèi)存空間,ptr是一個(gè)任意類(lèi)型的指針變量,它指向被釋放區(qū)域的首地址。被釋放區(qū)域應(yīng)是由malloc或calloc函數(shù)所分配的區(qū)域?!纠?.1.5】
3.鏈表
在上述例子中采用了動(dòng)態(tài)分配內(nèi)存空間的辦法為一個(gè)結(jié)構(gòu)體分配內(nèi)存空間。每一次分配一塊內(nèi)存空間可用來(lái)存放一個(gè)學(xué)生的數(shù)據(jù),我們可稱(chēng)之為一個(gè)結(jié)點(diǎn)。有多少個(gè)學(xué)生就應(yīng)該申請(qǐng)分配多少塊內(nèi)存空間,也就是說(shuō)要建立多少個(gè)結(jié)點(diǎn)。在不能確定學(xué)生記錄的情況下,無(wú)法用數(shù)組來(lái)實(shí)現(xiàn),為了解決這樣的問(wèn)題,最好的辦法就是動(dòng)態(tài)分配內(nèi)存空間。當(dāng)學(xué)生記錄不需要的時(shí)候,可以刪去該結(jié)點(diǎn),并釋放該結(jié)點(diǎn)占用的存儲(chǔ)空間。從而節(jié)約了寶貴的內(nèi)存資源。如果使用數(shù)組,則必須分配一塊連續(xù)的內(nèi)存區(qū)域。而使用動(dòng)態(tài)分配時(shí),每個(gè)結(jié)點(diǎn)之間可以是不連續(xù)的,結(jié)點(diǎn)之間的聯(lián)系可以用指針實(shí)現(xiàn)。即在結(jié)點(diǎn)結(jié)構(gòu)中定義一個(gè)成員項(xiàng)用來(lái)存放下一個(gè)結(jié)點(diǎn)的首地址,這個(gè)用于存放地址的成員,稱(chēng)為指針域??稍诘谝粋€(gè)結(jié)點(diǎn)的指針域內(nèi)存入第二個(gè)結(jié)點(diǎn)的首地址,在第二個(gè)結(jié)點(diǎn)的指針域內(nèi)又存放第三個(gè)結(jié)點(diǎn)的首地址,以此類(lèi)推直到最后一個(gè)結(jié)點(diǎn)。最后一個(gè)結(jié)點(diǎn)因無(wú)后續(xù)結(jié)點(diǎn)連接,故其指針域可賦為0。這樣一種連接方式,在數(shù)據(jù)結(jié)構(gòu)中稱(chēng)為“鏈表”。圖4-1-1是一個(gè)簡(jiǎn)單鏈表的示意圖。圖4-1-1簡(jiǎn)單鏈表示意圖在圖4-1-1中,第0個(gè)結(jié)點(diǎn)稱(chēng)為頭結(jié)點(diǎn),它存放有第一個(gè)結(jié)點(diǎn)的首地址,它沒(méi)有數(shù)據(jù),只是一個(gè)指針變量。以下的每個(gè)結(jié)點(diǎn)都分為兩個(gè)域,一個(gè)是數(shù)據(jù)域,存放各種實(shí)際的數(shù)據(jù),如學(xué)號(hào)num、姓名name、性別sex和成績(jī)score等。另一個(gè)域?yàn)橹羔樣?,存放下一結(jié)點(diǎn)的首地址。鏈表中的每一個(gè)結(jié)點(diǎn)都是同一種結(jié)構(gòu)類(lèi)型。
例如,創(chuàng)建一個(gè)學(xué)生學(xué)號(hào)和成績(jī)的結(jié)點(diǎn),該結(jié)點(diǎn)應(yīng)該具有如下結(jié)構(gòu):
structstu
{
intnum;
intscore;
structstu*next;
}前兩個(gè)成員項(xiàng)組成數(shù)據(jù)域,后一個(gè)成員項(xiàng)next構(gòu)成指針域,它是一個(gè)指向train類(lèi)型的結(jié)構(gòu)體指針變量。
對(duì)鏈表的主要操作有以下幾種:
①建立鏈表;
②結(jié)構(gòu)的查找與輸出;
③插入一個(gè)結(jié)點(diǎn);
④刪除一個(gè)結(jié)點(diǎn);
下面通過(guò)例題來(lái)說(shuō)明這些操作。
【例4.1.6】編寫(xiě)一個(gè)建立鏈表的函數(shù)creat,存放學(xué)生數(shù)據(jù)。
4.枚舉類(lèi)型
在學(xué)生管理系統(tǒng)中,我們限定學(xué)生的“政治面貌”有黨員、團(tuán)員和普通學(xué)生三種類(lèi)型,因此在操作時(shí)需要限定一定范圍。這和現(xiàn)實(shí)生活中的一些現(xiàn)象很相似,我們一周從星期一至星期日有七天,一年從一月到十二月有十二個(gè)月,這些情況都是不能超出范圍的。為了能更好的表述這種情況,C語(yǔ)言提供了一種稱(chēng)為“枚舉”的類(lèi)型。在“枚舉”類(lèi)型的定義中列舉出所有可能的取值,被說(shuō)明為該“枚舉”類(lèi)型的變量取值不能超過(guò)定義的范圍。1)枚舉的定義
枚舉類(lèi)型定義的一般形式為:
enum枚舉名{枚舉值表};
在枚舉值表中應(yīng)把所有可能用到的值都列出來(lái),我們把這些值也稱(chēng)為枚舉元素。
例如:
enumweekday{monday,tuesday,wednesday,thursday,friday,saturday,sunday};
該枚舉名為weekday,枚舉值共有7個(gè),即從星期一到星期日的七天。凡被說(shuō)明為weekday類(lèi)型變量的取值只能是七天中的某一天。
2)枚舉變量的說(shuō)明
枚舉變量也有幾種不同的說(shuō)明方式,即先定義后說(shuō)明或在定義的同時(shí)說(shuō)明。
設(shè)有變量a、b、c被說(shuō)明為上述的weekday,此時(shí)可以采用下述任一種方式。
先定義后說(shuō)明:
enumweekday{monday,tuesday,wednesday,thursday,friday,saturday,sunday};
enumweekdaya,b,c;
在定義的同時(shí)說(shuō)明:
enumweekday{monday,tuesday,wednesday,thursday,friday,saturday,sunday}a,b,c;
3)枚舉類(lèi)型變量的賦值和使用
枚舉類(lèi)型在使用中有以下規(guī)定:
枚舉值是常量,不是變量。不能在程序中用賦值語(yǔ)句再對(duì)它賦值,只能把枚舉值賦予枚舉變量,不能把元素的數(shù)值直接賦予枚舉變量。
例如對(duì)weekday的元素再作以下賦值:
monday=3;
是錯(cuò)誤的,如果一定要把數(shù)值賦予枚舉變量,則必須用強(qiáng)制類(lèi)型轉(zhuǎn)換。如下:
a=(enumweekday)3;
枚舉元素本身由系統(tǒng)定義了一個(gè)表示序號(hào)的數(shù)值,從0開(kāi)始順序定義為0,1,2…。如在weekday中,monday的值為0,tuesday的值為1,…,sunday的值為6。【例4.1.7】編程使用枚舉值。
五、任務(wù)小結(jié)
通過(guò)對(duì)“火車(chē)票務(wù)管理系統(tǒng)”中自定義數(shù)據(jù)類(lèi)型的使用,應(yīng)該掌握結(jié)構(gòu)體類(lèi)型的定義、引用等操作,同時(shí)拓展學(xué)習(xí)指針結(jié)構(gòu)類(lèi)型的使用、枚舉類(lèi)型的使用。
任務(wù)2查詢火車(chē)時(shí)刻信息
一、任務(wù)情境
在“火車(chē)票務(wù)管理系統(tǒng)”中,由于列車(chē)的信息量較大,列車(chē)的數(shù)據(jù)采用不同的數(shù)據(jù)類(lèi)型來(lái)存放。因此對(duì)信息的保存和操作要求較多,采用數(shù)組形式來(lái)保存數(shù)據(jù)已經(jīng)不能滿足要求,為此采用文件形式來(lái)存放數(shù)據(jù)。對(duì)文件信息的讀寫(xiě)將由一些函數(shù)來(lái)完成。因此,我們要掌握文件類(lèi)型及文件操作函數(shù)。
下面我們將設(shè)計(jì)一個(gè)火車(chē)票務(wù)管理系統(tǒng),實(shí)現(xiàn)火車(chē)時(shí)刻信息查詢,查詢的方法包括按照車(chē)次查詢、按終點(diǎn)查詢、按起點(diǎn)查詢、按終點(diǎn)和日期查詢。
二、相關(guān)知識(shí)
【例4.2.1】讀入文件record.txt,在屏幕上輸出文件相關(guān)信息。
1.?C語(yǔ)言文件的簡(jiǎn)介
“文件”可以被理解成一組存儲(chǔ)在外部介質(zhì)上的相關(guān)數(shù)據(jù)的有序集合。我們給這個(gè)數(shù)據(jù)集合取了一個(gè)名稱(chēng),叫做文件名。前面我們接觸到的源程序文件、目標(biāo)文件、可執(zhí)行文件、庫(kù)文件(頭文件)等都是文件。
C語(yǔ)言把一組有序的字符(字節(jié))稱(chēng)為文件,即由一個(gè)一個(gè)字符(字節(jié))的數(shù)據(jù)順序組成。因此,在C語(yǔ)言中根據(jù)數(shù)據(jù)的組織形式可以將文件分為ASCII碼文件和二進(jìn)制文件。ASCII碼文件又稱(chēng)文本文件,這種文件在磁盤(pán)中存放時(shí)每個(gè)字符對(duì)應(yīng)一個(gè)字節(jié),用于存放對(duì)應(yīng)的ASCII碼。二進(jìn)制文件是按二進(jìn)制的編碼方式來(lái)存放文件的。
在這里,我們將學(xué)習(xí)文件的打開(kāi)、關(guān)閉、讀、寫(xiě)、定位等各種操作。
2.文件類(lèi)型指針
在C語(yǔ)言中用一個(gè)指針變量指向一個(gè)文件,這個(gè)指針?lè)Q為文件指針。通過(guò)文件指針就可對(duì)它所指的文件進(jìn)行各種操作。
通過(guò)例4.2.1程序可以抽象出定義文件指針的一般形式:
FILE*指針變量標(biāo)識(shí)符;
其中FILE應(yīng)為大寫(xiě),它實(shí)際上是由系統(tǒng)定義的一個(gè)結(jié)構(gòu),該結(jié)構(gòu)的聲明在TurBoC中的stdio.h的文件中;它包含有文件名、文件狀態(tài)、緩沖區(qū)的大小和文件當(dāng)前位置等信息。在應(yīng)用和編寫(xiě)源程序時(shí)不必關(guān)心FILE結(jié)構(gòu)的細(xì)節(jié)。例如:
FILE*fp;
表示fp是指向FILE結(jié)構(gòu)的指針變量,通過(guò)fp即可找到存放某個(gè)文件信息的結(jié)構(gòu)變量,然后按結(jié)構(gòu)變量提供的信息找到該文件,實(shí)施對(duì)文件的操作。
3.文件的打開(kāi)與關(guān)閉操作
文件在進(jìn)行讀寫(xiě)操作之前要先“打開(kāi)”,再通過(guò)相應(yīng)的操作函數(shù)對(duì)文件進(jìn)行讀寫(xiě),使用結(jié)束之后,要把打開(kāi)的文件“關(guān)閉”,禁止再對(duì)該文件進(jìn)行操作。
下面我們分別介紹文件的打開(kāi)與關(guān)閉操作。
1)文件的打開(kāi)(fopen函數(shù))
fopen函數(shù)用來(lái)打開(kāi)一個(gè)文件,其調(diào)用的一般形式為:
文件指針名=fopen(文件名,使用文件方式);
其中,“文件指針名”必須是被說(shuō)明為FILE類(lèi)型的指針變量;
“文件名”是被打開(kāi)文件的文件名,一般為字符串常量或字符串?dāng)?shù)組。
“使用文件方式”是指文件的類(lèi)型和操作要求。例如:
FILE*fp;
fp=(“filestudent”,“r”);
其意義是在當(dāng)前目錄下打開(kāi)文件filestudent,只允許進(jìn)行“讀”操作,并使fp指向該文件。
又如:
FILE*fpinfo;
fpinfo=fopen(“e:\\information”,“rb”);
其意義是打開(kāi)E磁盤(pán)驅(qū)動(dòng)器的根目錄下的文件information,這是一個(gè)二進(jìn)制文件,只允許按二進(jìn)制方式進(jìn)行讀操作。兩個(gè)反斜線“\\”中的第一個(gè)表示轉(zhuǎn)義字符,第二個(gè)表示根目錄。
當(dāng)使用fopen()函數(shù)打開(kāi)文件操作不能實(shí)現(xiàn)時(shí),該函數(shù)會(huì)傳遞回一個(gè)出錯(cuò)信息。出錯(cuò)的原因可能是:用“r”打開(kāi)的文件并不存在、磁盤(pán)故障、磁盤(pán)已滿、空間不足、無(wú)法建立新文件等。此時(shí),fopen函數(shù)會(huì)帶回一個(gè)空的指針值NULL(NULL在stdio.h文件中已被定義為0)。如:
if(fp=fopen(“file1”,“r”)==NULL)
{
printf(“cannotopenthisfile\n”);
exit(0);
}
程序運(yùn)行時(shí),首先檢查打開(kāi)文件操作是否出錯(cuò)。當(dāng)出錯(cuò)時(shí),就會(huì)在終端上輸出“cannotopenthisfile”。exit函數(shù)就會(huì)關(guān)閉所有文件,終止正在執(zhí)行的程序,等待用戶去檢查并修復(fù)錯(cuò)誤,然后再運(yùn)行。
使用文件的方式共有12種,在表4-2-1中列舉出了各個(gè)符號(hào)及其含義并簡(jiǎn)單舉例。在表中假定fp是FILE類(lèi)型,file1是要操作的文件名。使用“r”方式時(shí)(read:讀),要打開(kāi)的文件必須存在,當(dāng)該輸入文件不存在時(shí)會(huì)出錯(cuò);不能向該文件輸出數(shù)據(jù),只能利用該文件向計(jì)算機(jī)輸入數(shù)據(jù)。
使用“w”方式時(shí)(write:寫(xiě)),要打開(kāi)的文件可以存在,也可以不存在;當(dāng)該文件存在時(shí),使用“w”方式打開(kāi)文件時(shí),系統(tǒng)會(huì)把已經(jīng)存在的文件刪掉,然后再重新建立一個(gè)新的文件,該文件名就是要操作的文件名;如果要操作的文件不存在,在打開(kāi)時(shí)會(huì)自動(dòng)創(chuàng)建一個(gè)指定名稱(chēng)的文件,同時(shí)不能利用該文件向計(jì)算機(jī)輸入數(shù)據(jù),只能用于向該文件寫(xiě)入
數(shù)據(jù)。
使用“a”方式時(shí)(append:追加),要打開(kāi)的文件必須存在,當(dāng)該文件不存在時(shí)會(huì)出錯(cuò);該打開(kāi)方式不會(huì)刪除原有數(shù)據(jù),只會(huì)向文件尾部添加新的記錄。因此在打開(kāi)文件時(shí),位置指針會(huì)自動(dòng)移動(dòng)到文件末尾。這種打開(kāi)方式能保證原來(lái)數(shù)據(jù)的安全。使用“r+”、“w+”、“a+”方式時(shí),這三種方式所具有的共同特點(diǎn)是打開(kāi)的文件既可以作為輸入文件向計(jì)算機(jī)輸入數(shù)據(jù),也可以作為輸出文件,向文件中寫(xiě)入數(shù)據(jù)。三種打開(kāi)方式的不同點(diǎn)是,用“r+”方式時(shí),要操作的文件已經(jīng)存在,用該文件向計(jì)算機(jī)輸入數(shù)據(jù)。用“w+”方式時(shí),系統(tǒng)會(huì)自動(dòng)新建一個(gè)文件,先向此文件寫(xiě)入數(shù)據(jù),然后可以從該文件中讀取數(shù)據(jù)。用“a+”方式時(shí),被打開(kāi)的原文件不被刪除,文件位置指針自動(dòng)移動(dòng)到文件末尾,向文件添加數(shù)據(jù),也可以讀取數(shù)據(jù)。
2)文件關(guān)閉函數(shù)(fclose函數(shù))
文件一旦使用完畢,應(yīng)用關(guān)閉文件函數(shù)把文件關(guān)閉,以避免文件的數(shù)據(jù)丟失。
fclose函數(shù)用來(lái)關(guān)閉文件,其調(diào)用的一般形式是:
fclose(文件指針);
例如:
fclose(fp);
當(dāng)執(zhí)行關(guān)閉操作時(shí),是通過(guò)fp把文件關(guān)閉的,即fp不再指向該文件。編程人員應(yīng)該養(yǎng)成一個(gè)良好的編程習(xí)慣,在程序終止前關(guān)閉所打開(kāi)的文件,以免文件中的數(shù)據(jù)遭到破壞或者丟失。正常完成關(guān)閉文件操作時(shí),fclose函數(shù)返回值為0。否則會(huì)返回一個(gè)非0的值EOF(-1)??梢哉{(diào)用ferror函數(shù)來(lái)測(cè)試該值。
4.文件的讀寫(xiě)
在C語(yǔ)言中,文件的讀寫(xiě)操作是由函數(shù)來(lái)完成的,C語(yǔ)言提供的多種文件讀寫(xiě)的函數(shù)如表4-2-2所示。
1)字符讀寫(xiě)函數(shù)fgetc和fputc
字符讀寫(xiě)函數(shù)是以字符為單位進(jìn)行的讀寫(xiě)操作,即每次可向文件寫(xiě)入或從文件讀出單個(gè)字符,而不是字符串。
(1)讀字符函數(shù)fgetc。fgetc函數(shù)的功能是從指定的文件中讀一個(gè)字符,該打開(kāi)文件必須是以讀或讀寫(xiě)的方式打開(kāi)的,否則會(huì)出錯(cuò)。函數(shù)調(diào)用的形式為:
字符變量=fgetc(文件指針);
例如:
ch=fgetc(fp);在這條語(yǔ)句中,ch是一個(gè)字符變量,fp是一個(gè)文件型指針變量。當(dāng)fgetc函數(shù)讀取字符數(shù)據(jù)遇到文件結(jié)束符時(shí),函數(shù)會(huì)返回一個(gè)文件結(jié)束標(biāo)志EOF(-1)。由于EOF是不可輸出的字符,它不能在屏幕上顯示,因此將EOF定義成?-1就能解決屏幕顯示的問(wèn)題。在讀入某個(gè)字節(jié)中二進(jìn)制數(shù)據(jù)的時(shí)候,由于該位字符可能是?-1,這就產(chǎn)生了矛盾。為了解決這個(gè)矛盾,C語(yǔ)言提供了一個(gè)判斷文件是否結(jié)束的函數(shù)feof()。feof(fp)測(cè)試fp所指向的文件是否到結(jié)束,當(dāng)文件結(jié)束時(shí),feof(fp)函數(shù)值為1(真),否則為0(假)。下面分別舉例說(shuō)明如何在程序中使用EOF和feof()函數(shù)。以下程序完成從文件中順序讀入字符并在屏幕上顯示出來(lái)的功能:
ch=fgetc(fp);
while(ch!=EOF)
{
putchar(ch);
ch=fgetc(fp);
}
在文件內(nèi)部有一個(gè)位置指針,用來(lái)指向文件的當(dāng)前讀寫(xiě)字節(jié)。在文件打開(kāi)時(shí),該指針總是指向文件的第一個(gè)字節(jié)。使用fgetc函數(shù)后,該位置指針將向后移動(dòng)一個(gè)字節(jié)。以下程序完成順序讀入一個(gè)二進(jìn)制文件中的數(shù)據(jù)的功能:
…
while(!feof(fp))
{
c=fgetc(fp);
…
}
在程序中,c為整型變量。當(dāng)文件結(jié)束時(shí),feof(fp)值為1,否則值為0。在循環(huán)中每次判斷是否到結(jié)束狀態(tài),當(dāng)遇到結(jié)束標(biāo)識(shí)時(shí),循環(huán)不再執(zhí)行,否則會(huì)一直執(zhí)行下去。這種方法不僅適用于二進(jìn)制文件,同時(shí)也適用于文本文件。在程序中,定義了兩個(gè)變量:fp是文件類(lèi)型指針變量,用來(lái)存放文件打開(kāi)的首地址;ch是字符型變量,用來(lái)存放取得當(dāng)前位置上的字符數(shù)據(jù)。程序中以讀文本文件方式打開(kāi)文件“record.txt”,并使fp指向該文件。當(dāng)打開(kāi)文件出錯(cuò)時(shí),會(huì)輸出提示并關(guān)閉文件,退出程序。程序先讀取一個(gè)字符,然后進(jìn)入循環(huán),若循環(huán)判斷讀出的字符不是文件結(jié)束標(biāo)志(EOF),就把該字符顯示在屏幕上,再讀入下一字符。每讀一次,文件內(nèi)部的位置指針向后移動(dòng)一個(gè)字符,文件結(jié)束時(shí),該指針指向EOF。程序執(zhí)行完畢,會(huì)將整個(gè)學(xué)生信息全部輸出到屏幕上。
(2)寫(xiě)字符函數(shù)fputc。fputc函數(shù)的功能是把一個(gè)字符寫(xiě)入指定的磁盤(pán)文件中,函數(shù)調(diào)用的一般形式為:
fputc(字符量,文件指針);
其中,待寫(xiě)入的字符量可以是字符常量或變量,例如:
fputc('k',fp);
其意義是把字符k寫(xiě)入fp所指向的文件中。注意:對(duì)要寫(xiě)入的文件可以用寫(xiě)、讀寫(xiě)、追加方式打開(kāi),用寫(xiě)或讀寫(xiě)方式打開(kāi)一個(gè)已存在的文件時(shí)將會(huì)清除原有的文件內(nèi)容,寫(xiě)入字符從文件首開(kāi)始。如需保留原有文件內(nèi)容,希望寫(xiě)入的字符從文件末開(kāi)始存放,必須以追加方式打開(kāi)文件。被寫(xiě)入的文件若不存在,則創(chuàng)建該文件。每寫(xiě)入一個(gè)字符,文件內(nèi)部位置指針向后移動(dòng)一個(gè)字節(jié)。當(dāng)調(diào)用fputc函數(shù)時(shí)會(huì)有一個(gè)返回值,如寫(xiě)入成功則返回寫(xiě)入的字符,否則返回EOF??捎么藖?lái)判斷寫(xiě)入是否成功。
【例4.2.3】從鍵盤(pán)輸入一行列車(chē)數(shù)據(jù),寫(xiě)入到record.txt文件中,再把該文件內(nèi)容讀出顯示在屏幕上。在程序中,定義了兩個(gè)變量:fp是文件類(lèi)型指針變量,用來(lái)存放文件打開(kāi)的首地址;ch是字符型變量,用來(lái)存放取得當(dāng)前位置上的字符數(shù)據(jù)。程序中以讀寫(xiě)方式打開(kāi)文件“e:\\record.txt”,并使fp指向該文件。當(dāng)打開(kāi)文件出錯(cuò)時(shí),會(huì)輸出提示并關(guān)閉文件,退出程序。程序執(zhí)行過(guò)程是將鍵盤(pán)輸入的數(shù)據(jù)寫(xiě)入到指定的文件,然后再?gòu)奈募凶x取數(shù)據(jù),將整個(gè)列車(chē)信息全部輸出到屏幕上。
2)字符串讀寫(xiě)函數(shù)fgets和fputs
字符串讀寫(xiě)函數(shù)是以字符串為單位進(jìn)行的讀寫(xiě)操作,即每次可向文件寫(xiě)入或從文件讀出一個(gè)字符串。
(1)讀字符串函數(shù)fgets。函數(shù)的功能是從指定的文件中讀一個(gè)字符串到字符數(shù)組中,函數(shù)調(diào)用的形式為:
fgets(字符數(shù)組名,n,文件指針);
其中的n是一個(gè)正整數(shù),表示從文件中讀出的字符串不超過(guò)n-1個(gè)字符。在讀入的最后一個(gè)字符后加上串結(jié)束標(biāo)志'\0'。假如在讀取n-1個(gè)字符之前遇到了換行或EOF,讀入操作結(jié)束,此時(shí)fgets函數(shù)將把str的首地址返回。例如:
fgets(str,n,fp);
該語(yǔ)句的含義是從fp所指的文件中讀出n-1個(gè)字符送入字符數(shù)組str中。在程序中,定義了兩個(gè)變量:fp是文件類(lèi)型指針變量,用來(lái)存放文件打開(kāi)的首地址;str是字符型數(shù)組,用來(lái)存放讀取到的字符串?dāng)?shù)據(jù)。程序中以讀方式打開(kāi)文件“e:\trainfile.txt”,并使fp指向該文件。當(dāng)打開(kāi)文件出錯(cuò)時(shí),會(huì)輸出提示并關(guān)閉文件,退出程序。程序執(zhí)行過(guò)程是從打開(kāi)的文件中讀出20個(gè)字符送入str數(shù)組,在數(shù)組最后一個(gè)單元內(nèi)將加上‘\0’,然后在屏幕上顯示輸出str數(shù)組。
注意:使用fgets函數(shù)也有返回值,其返回值是字符數(shù)組的首地址。如果在讀出n-1個(gè)字符之前就遇到了換行符或EOF,則讀出操作結(jié)束。
(2)寫(xiě)字符串函數(shù)fputs。fputs函數(shù)的功能是向指定的文件寫(xiě)入一個(gè)字符串,函數(shù)調(diào)用的一般形式為:
fputs(字符串,文件指針);
其中,字符串可以是字符串常量,也可以是字符數(shù)組名或指針變量。當(dāng)輸出成功時(shí),函數(shù)值為0;當(dāng)輸出失敗時(shí),函數(shù)值為EOF。
例如:
fputs("student_name",fp);
其意義是把字符串“student_name”寫(xiě)入fp所指的文件中。在程序中定義了三個(gè)變量:fp是文件類(lèi)型指針變量,用來(lái)存放文件打開(kāi)的首地址;ch是字符型變量,用來(lái)存放從文件中讀取的字符;str是字符型數(shù)組,用來(lái)存放從鍵盤(pán)讀取到的字符串?dāng)?shù)據(jù)。程序中以追加方式打開(kāi)文件“e:\trainfile.txt”,并使fp指向該文件。當(dāng)打開(kāi)文件出錯(cuò)時(shí),會(huì)輸出提示并關(guān)閉文件,退出程序。程序執(zhí)行過(guò)程是從鍵盤(pán)讀取一串字符送入str數(shù)組,然后將該字符串追加到打開(kāi)文件的末尾,然后從指定文件中讀取字符串信息,輸出到屏幕上,最后關(guān)閉文件。
3)數(shù)據(jù)塊讀寫(xiě)函數(shù)fread和fwtrite
C語(yǔ)言還提供了用于整塊數(shù)據(jù)讀寫(xiě)的函數(shù),可用來(lái)讀寫(xiě)一組數(shù)據(jù),如一個(gè)數(shù)組元素、一個(gè)結(jié)構(gòu)變量的值等。
讀數(shù)據(jù)塊函數(shù)調(diào)用的一般形式為:
fread(buffer,size,count,fp);
寫(xiě)數(shù)據(jù)塊函數(shù)調(diào)用的一般形式為:
fwrite(buffer,size,count,fp);
其中:
buffer是一個(gè)指針。在fread函數(shù)中,它表示存放輸入數(shù)據(jù)的首地址;在fwrite函數(shù)中,它表示存放輸出數(shù)據(jù)的首地址。
size表示數(shù)據(jù)塊的字節(jié)數(shù)。
count表示要讀寫(xiě)的數(shù)據(jù)塊塊數(shù)。
fp表示文件型指針。
注意:fread和fwrite函數(shù)一般用于二進(jìn)制文件的輸入與輸出。當(dāng)fread和fwrite調(diào)用成功時(shí),函數(shù)返回值為count的值,即輸入或輸出數(shù)據(jù)項(xiàng)的完整個(gè)數(shù)。
例如:
fread(fa,4,8,fp);
其意義是,從fp所指的文件中每次讀4個(gè)字節(jié)(一個(gè)實(shí)數(shù))送入實(shí)數(shù)組fa中,連續(xù)讀8次,即讀8個(gè)實(shí)數(shù)到fa中。
【例4.2.6】定義一個(gè)有關(guān)學(xué)生的結(jié)構(gòu)類(lèi)型,從鍵盤(pán)讀取兩個(gè)學(xué)生數(shù)據(jù),寫(xiě)入到文件中,再將這兩個(gè)數(shù)據(jù)顯示在屏幕上。在程序中,定義了一個(gè)結(jié)構(gòu)student,說(shuō)明了兩個(gè)結(jié)構(gòu)數(shù)組student1和student2以及兩個(gè)結(jié)構(gòu)指針變量pp和qq。pp指向student1,qq指向student2。在main函數(shù)中定義了三個(gè)變量:fp是文件類(lèi)型指針變量,用來(lái)存放文件打開(kāi)的首地址;ch是字符型變量,用來(lái)存放從文件中讀取的字符;i是一個(gè)整型變量,用來(lái)作為循環(huán)控制變量。第16行以讀寫(xiě)方式打開(kāi)二進(jìn)制文件“stufile”,輸入兩個(gè)學(xué)生數(shù)據(jù)之后,將學(xué)生數(shù)據(jù)寫(xiě)入該文件中。然后把文件內(nèi)部位置指針移到文件首,讀出兩塊學(xué)生數(shù)據(jù)后,在屏幕上顯示出來(lái)。三、任務(wù)實(shí)施
通過(guò)相關(guān)理論學(xué)習(xí)后,我們就可以應(yīng)用“火車(chē)票務(wù)管理系統(tǒng)”中用到的文件了。
1.確定查詢火車(chē)的功能類(lèi)型
voidSearchTrainNum();
//按照車(chē)次查詢
voidSearchToPlace();?
//按終點(diǎn)查詢
voidSearchFromPlace();
//按起點(diǎn)查詢
voidSearchToPlaceAndDate();//按終點(diǎn)和日期查詢2.利用文件完成查詢操作
1)按照車(chē)次查詢
voidSearchTrainNum()
{
FILE*fp1;
charnum[20];
charch;
inti=0;
fp1=fopen(".\\train.rec","r+");
if(!fp1)
{
printf("Filecannotbeopened\n");
exit(1);
}
do
{
rewind(fp1);//文件指針的操作,到文件首函數(shù)
printf("輸入你要查找的車(chē)次號(hào).\n>>>");
scanf("%s",num);
while(!feof(fp1))
{
fread(&train[i],sizeof(structTRAIN),1,fp1);
if(strcmp(num,train[i].trainNum)==0)
{
printf("%-4s%-12s%-8s%-10s%-10s%-8s%.2lf",train[i].trainNum,train[i].date,train[i].fromPlace,train[i].toPlace,train[i].inTime,train[i].outTime,train[i].price); printf("\n");
}
i++;
}
printf("continue(Y/N)(Ntoend)?");
getchar();
scanf("%c",&ch);
}while(ch!='N');
printf("\n");
fclose(fp1);
}2)按終點(diǎn)查詢
voidSearchToPlace()
{
FILE*fp2;
charnum[20];
charch;
inti=0;
fp2=fopen(".\\train.rec","r+");
if(!fp2)
{
printf("Filecannotbeopened\n");
exit(1);
}
do
{
rewind(fp2);
printf(“輸入你要查詢的終點(diǎn).\n>>>”);
scanf(“%s”,num);
while(!feof(fp2))
{
fread(&train[i],sizeof(structTRAIN),1,fp2);
if(strcmp(num,train[i].toPlace)==0)
{
printf(“%-4s%-12s%-8s%-10s%-10s%-8s%.2lf”,
train[i].trainNum,train[i].date,
train[i].fromPlace,train[i].toPlace,train[i].inTime,
train[i].outTime,train[i].price);
printf("\n");
}
i++;
}
printf(“continue(Y/N)(Ntoend)?”);
getchar();
scanf("%c",&ch);
}while(ch!='N');
printf("\n");
fclose(fp2);
}3)按起點(diǎn)查詢
voidSearchFromPlace()
{
FILE*fp3;
charnum[20];
charch;
inti=0;
fp3=fopen(".\\train.rec","r+");
if(!fp3)
{
printf("Filecannotbeopened\n");
exit(1);
}
do
{rewind(fp3);
printf("輸入你要查詢的起點(diǎn).\n>>>");
scanf("%s",num);
while(!feof(fp3))
{
fread(&train[i],sizeof(structTRAIN),1,fp3);
if(strcmp(num,train[i].fromPlace)==0)
{
printf("%-4s%-12s%-8s%-10s%-10s%-8s%.2lf",train[i].trainNum,train[i].date,
train[i].fromPlace,train[i].toPlace,train[i].inTime,train[i].outTime,
train[i].price);
printf("\n");
}
i++;
}
printf("continue(Y/N)(Ntoend)?");
getchar();
scanf("%c",&ch);
}while(ch!='N');
printf("\n");
fclose(fp3);
}4)按終點(diǎn)和日期查詢
voidSearchToPlaceAndDate()
{
FILE*fp4;
charnum[20];
charnum1[20];
charch;
inti=0;
fp4=fopen(“.\\train.rec”,“r+”);
if(!fp4)
{
printf("Filecannotbeopened\n");
exit(1);
}
do
{
rewind(fp4);
printf("輸入你要查詢的終點(diǎn)與日期.\n>>>");
scanf("%s",num);
scanf("%s",num1);
while(!feof(fp4))
{
fread(&train[i],sizeof(structTRAIN),1,fp4);
if((strcmp(num,train[i].toPlace)==0)&&(strcmp(num1,train[i].date)==0))
{
printf("%-4s%-12s%-8s%-10s%-10s%-8s%.2lf",train[i].trainNum,train[i].date,
train[i].fromPlace,train[i].toPlace,train[i].inTime,train[i].outTime,train[i].price);
printf("\n");
}
i++;
}
printf("continue(Y/N)(Ntoend)?");
getchar();
scanf("%c",&ch);
}while(ch!='N');
printf("\n");
fclose(fp4);
}四、知識(shí)拓展
前面介紹的對(duì)文件的讀寫(xiě)方式都是順序讀寫(xiě),即讀寫(xiě)文件只能從頭開(kāi)始,順序讀寫(xiě)各個(gè)數(shù)據(jù)。但在實(shí)際問(wèn)題中常要求只讀寫(xiě)文件中某一指定的部分。為了解決這個(gè)問(wèn)題,可移動(dòng)文件內(nèi)部的位置指針到需要讀寫(xiě)的位置,再進(jìn)行讀寫(xiě),這種讀寫(xiě)方式稱(chēng)為隨機(jī)讀寫(xiě)。實(shí)現(xiàn)隨機(jī)讀寫(xiě)的關(guān)鍵是要按要求移動(dòng)位置指針,這稱(chēng)為文件的定位。
1.文件定位
移動(dòng)文件內(nèi)部位置指針的函數(shù)主要有兩個(gè),即rewind函數(shù)和fseek函數(shù)。
rewind函數(shù)的功能是把文件內(nèi)部的位置指針移到文件首,其調(diào)用形式為:
rewind(文件指針);
fseek函數(shù)用來(lái)移動(dòng)文件內(nèi)部位置指針,其調(diào)用形式為:
fseek(文件指針,位移量,起始點(diǎn));
其中:
“文件指針”指向被移動(dòng)的文件。
“位移量”表示移動(dòng)的字節(jié)數(shù),要求位移量是long型數(shù)據(jù),以便在文件長(zhǎng)度大于64?KB時(shí)不會(huì)出錯(cuò)。當(dāng)用常量表示位移量時(shí),要求加后綴“L”。
“起始點(diǎn)”表示從何處開(kāi)始計(jì)算位移量。規(guī)定的起始點(diǎn)有三種:文件首、當(dāng)前位置和文件尾。其表示方法如表4-2-3所示。例如:
fseek(fp,50L,0);
其意義是把位置指針移到離文件首50個(gè)字節(jié)處。
注意:fseek函數(shù)一般用于二進(jìn)制文件。在文本文件中如果要使用fseek函數(shù),由于要進(jìn)行轉(zhuǎn)換,故往往計(jì)算的位置會(huì)出現(xiàn)錯(cuò)誤。
2.文件的隨機(jī)讀寫(xiě)
在移動(dòng)位置指針之后,即可用前面介紹的任一種讀寫(xiě)函數(shù)進(jìn)行讀寫(xiě)。由于一般是讀寫(xiě)一個(gè)數(shù)據(jù)塊,因此常用fread和fwrite函數(shù)。
下面用例題來(lái)說(shuō)明文件的隨機(jī)讀寫(xiě)。
【例4.2.7】從學(xué)生文件studentfile中讀出第二個(gè)學(xué)生的數(shù)據(jù),并將讀出的數(shù)據(jù)顯示在屏幕上。在程序中,定義了一個(gè)結(jié)構(gòu)student,聲明了一個(gè)結(jié)構(gòu)指針變量qq和一個(gè)結(jié)構(gòu)變量student2。qq指向student2。在main函數(shù)中定義了三個(gè)變量:fp是文件類(lèi)型指針變量,用來(lái)存放文件打開(kāi)的首地址;ch是字符型變量,用來(lái)存放從文件中讀取的字符;i是一個(gè)整型變量,用來(lái)指示指針移動(dòng)的個(gè)數(shù)。程序第15行以讀方式打開(kāi)二進(jìn)制文件“studentfile”,其中的i值為1,表示從文件首開(kāi)始,移動(dòng)一個(gè)student類(lèi)型的長(zhǎng)度,然后再讀出的數(shù)據(jù)即為第二個(gè)學(xué)生的數(shù)據(jù),將結(jié)果在屏幕上顯示。
3.流式文件的定位函數(shù)ftell
當(dāng)流式文件中的位置指針移動(dòng)的時(shí)候,編程人員不容易知道該指針當(dāng)前所處的位置,為此我們使用ftell函數(shù)來(lái)得到當(dāng)前指針的位置。ftell函數(shù)的作用就是獲得流式文件指針當(dāng)前位置值。當(dāng)ftell函數(shù)返回值為?-1L時(shí),表示已經(jīng)出錯(cuò)。
例如:
n=ftell(fp);
if(n==-1L)
printf("haveanerror\n");
我們使用n來(lái)存放當(dāng)前位置。當(dāng)調(diào)用函數(shù)出現(xiàn)錯(cuò)誤時(shí),則輸出“haveanerror”。
4.文件檢測(cè)函數(shù)
在C語(yǔ)言中,除了前面提到的讀寫(xiě)函數(shù)和定位函數(shù)之外,還提供了一些用來(lái)檢查輸入/輸出錯(cuò)誤的函數(shù)。這些檢測(cè)函數(shù)有feof、ferror、clearerr。
1)文件結(jié)束檢測(cè)函數(shù)feof
在C語(yǔ)言中,為了判斷文件是否處于結(jié)束位置,可以使用文件結(jié)束檢測(cè)函數(shù)feof來(lái)檢測(cè)。
feof函數(shù)調(diào)用的一般格式為:
feof(文件指針);
例如:
feof(fp);
功能:判斷文件是否處于文件結(jié)束位置。如文件結(jié)束,則返回值為真(非0),否則為假(0值)。
2)文件讀寫(xiě)出錯(cuò)檢測(cè)函數(shù)ferror
在C語(yǔ)言中,調(diào)用各種輸入/輸出函數(shù)的時(shí)候,當(dāng)出現(xiàn)錯(cuò)誤時(shí),除了用函數(shù)本身的返回值來(lái)反映之外,還可以使用ferror函數(shù)來(lái)檢查。
ferror函數(shù)調(diào)用的一般格式為:
ferror(文件指針);
例如:
ferror(fp);
功能:檢查文件在用各種輸入/輸出函數(shù)進(jìn)行讀寫(xiě)時(shí)是否出錯(cuò)。如ferror返回值為0表示未出錯(cuò),否則表示有錯(cuò)。
注意:當(dāng)執(zhí)行fopen函數(shù)時(shí),ferror函數(shù)自動(dòng)將初始值設(shè)置為0。在同一個(gè)文件中每調(diào)用一次輸入/輸出函數(shù),都會(huì)產(chǎn)生一個(gè)新的ferror函數(shù)值。為了防止信息丟失,在調(diào)用一個(gè)輸入/輸出函數(shù)后應(yīng)該立即檢查ferror函數(shù)的值。
3)?clearerr函數(shù)
在C語(yǔ)言中,當(dāng)調(diào)用輸入/輸出函數(shù)出現(xiàn)錯(cuò)誤的時(shí)候,ferror函數(shù)值為一個(gè)非零值,只要出現(xiàn)了錯(cuò)誤標(biāo)志,就將一直保留。為了解決這樣的問(wèn)題,我們可以使用clearerr函數(shù)將文件錯(cuò)誤標(biāo)志或文件結(jié)束標(biāo)志設(shè)置為0。也可用rewind函數(shù)或者其他輸入/輸出函數(shù)。
clearerr函數(shù)調(diào)用的一般格式為:
clearerr(文件指針);
例如:
clearerr(fp);
功能:使文件錯(cuò)誤標(biāo)志和文件結(jié)束標(biāo)志設(shè)置為0。五、任務(wù)小結(jié)
通過(guò)查詢火車(chē)時(shí)刻信息,應(yīng)該掌握文件讀取函數(shù)的使用方法。同時(shí)拓展學(xué)習(xí)文件定位函數(shù)的使用、文件檢測(cè)函數(shù)及文件出錯(cuò)標(biāo)志和文件結(jié)束標(biāo)志置0函數(shù)的使用。
任務(wù)3統(tǒng)計(jì)火車(chē)車(chē)次
一、任務(wù)情境
在前面的項(xiàng)目中,已多次使用過(guò)以“#”號(hào)開(kāi)頭的命令,如包含命令#include,宏定義命令#define等,這些命令稱(chēng)為預(yù)處理命令。在源程序中這些命令都放在函數(shù)之外,而且一般都放在源文件的前面,稱(chēng)為預(yù)處理部分。二、相關(guān)知識(shí)
【例4.3.1】該程序中,#defineM(y*y+3*y)是宏定義,它的作用是指定標(biāo)識(shí)符M來(lái)代替表達(dá)式(y*y+3*y)。在編寫(xiě)源程序時(shí),所有的(y*y+3*y)都可由M代替,而對(duì)源程序作編譯時(shí),將先由預(yù)處理程序進(jìn)行宏代換,即用(y*y+3*y)表達(dá)式去置換所有的宏名M,然后再進(jìn)行編譯。在預(yù)處理時(shí)經(jīng)宏展開(kāi)后該語(yǔ)句變?yōu)椋?/p>
s=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y);所謂預(yù)處理是指在進(jìn)行編譯的第一遍掃描(詞法掃描和語(yǔ)法分析)之前所做的工作。當(dāng)對(duì)一個(gè)源文件進(jìn)行編譯時(shí),系統(tǒng)將自動(dòng)引用預(yù)處理程序?qū)υ闯绦蛑械念A(yù)處理部分作處理,處理完畢后自動(dòng)進(jìn)入對(duì)源程序的編譯。
C語(yǔ)言提供了多種預(yù)處理功能,如宏定義、文件包含、條件編譯等。合理地使用預(yù)處理功能,可使編寫(xiě)的程序便于閱讀、修改、移植和調(diào)試,也有利于模塊化程序設(shè)計(jì)。
1.宏定義
在C語(yǔ)言源程序中允許用一個(gè)標(biāo)識(shí)符來(lái)表示一個(gè)字符串,稱(chēng)為“宏”。被定義為“宏”的標(biāo)識(shí)符稱(chēng)為“宏名”。在編譯預(yù)處理時(shí),對(duì)程序中所有出現(xiàn)的“宏名”,都用宏定義中的字符串去代換,這稱(chēng)為“宏代換”或“宏展開(kāi)”。
宏定義是由源程序中的宏定義命令完成的。宏代換是由預(yù)處理程序自動(dòng)完成的。例如:
#defineM(y*y+3*y)
說(shuō)明:
①宏定義是用宏名來(lái)表示一個(gè)字符串,在宏展開(kāi)時(shí)又以該字符串取代宏名,這只是一種簡(jiǎn)單的代換,字符串中可以含任何字符,可以是常數(shù),也可以是表達(dá)式,預(yù)處理程序?qū)λ蛔魅魏螜z查。
②宏定義不是說(shuō)明或語(yǔ)句,在行末不必加分號(hào),如加上分號(hào)則連分號(hào)也一起置換。
③宏定義必須寫(xiě)在函數(shù)之外,其作用域?yàn)楹甓x命令起到源程序結(jié)束。如要終止其作用域可使用#undef命令。④宏定義允許嵌套,在宏定義的字符串中可以使用已經(jīng)定義的宏名。在宏展開(kāi)時(shí)由預(yù)處理程序?qū)訉哟鷵Q。
例如:
#definePI3.1415926
#defineSPI*y*y/*PI是已定義的宏名*/
⑤習(xí)慣上宏名用大寫(xiě)字母表示,以便與變量區(qū)別。但也允許用小寫(xiě)字母。
⑥可用宏定義表示數(shù)據(jù)類(lèi)型,以使書(shū)寫(xiě)方便。
例如:
#defineSTUstructstu
在程序中可用STU作變量說(shuō)明:
STUbody[5],*p;
2)帶參宏定義
在宏定義中的參數(shù)稱(chēng)為形式參數(shù),在宏調(diào)用中的參數(shù)稱(chēng)為實(shí)際參數(shù)。對(duì)帶參數(shù)的宏,在調(diào)用中,不僅要進(jìn)行宏展開(kāi),而且要用實(shí)參去代換形參。
帶參宏定義的一般形式為:
#define宏名(形參表)字符串
帶參宏調(diào)用的一般形式為:
宏名(實(shí)參表);
例如:
#defineM(y)y*y+3*y /*宏定義*/
……
k=M(5); /*宏調(diào)用*/
……
在宏調(diào)用時(shí),用實(shí)參5去代替形參y,經(jīng)預(yù)處理宏展開(kāi)后的語(yǔ)句為:
k=5*5+3*5說(shuō)明:
①在帶參宏定義中,宏名和形參表之間不能有空格出現(xiàn)。
②在帶參宏定義中,形式參數(shù)不分配內(nèi)存單元,因此不必作類(lèi)型說(shuō)明。而宏調(diào)用中的實(shí)參有具體的值,要用它們?nèi)ゴ鷵Q形參,因此必須作類(lèi)型說(shuō)明。
③在宏定義中的形參是標(biāo)識(shí)符,而宏調(diào)用中的實(shí)參可以是表達(dá)式。
④在宏定義中,字符串內(nèi)的形參通常要用括號(hào)括起來(lái)以避免出錯(cuò)。在上例的宏定義中,(y)*(y)表達(dá)式的y都用括號(hào)括起來(lái)。
⑤帶參的宏和帶參函數(shù)很相似,但有本質(zhì)上的不同,除上面已談到的各點(diǎn)外,把同一表達(dá)式用函數(shù)處理與用宏處理兩者的結(jié)果有可能是不同的。
2.文件包含
文件包含命令行的一般形式為:
#include“文件名”
在前面我們已多次用此命令包含過(guò)庫(kù)函數(shù)的頭文件。
例如:
#include“stdio.h”
#include“math.h”
文件包含命令的功能是把指定的文件插入該命令行位置從而取代該命令行,從而把指定的文件和當(dāng)前的源程序文件連成一個(gè)源文件。在程序設(shè)計(jì)中,文件包含是很有用的。一個(gè)大的程序可以分為多個(gè)模塊,由多個(gè)程序員分別編程。有些公用的符號(hào)常量或宏定義等可單獨(dú)組成一個(gè)文件,在其他文件的開(kāi)頭用包含命令包含該文件即可使用。這樣,可避免在每個(gè)文件開(kāi)頭都去書(shū)寫(xiě)那些公用量,從而節(jié)省時(shí)間,并減少出錯(cuò)。說(shuō)明:
(1)包含命令中的文件名可以用雙引號(hào)括起來(lái),也可以用尖括號(hào)括起來(lái)。例如:
#include“stdio.h”
#include<math.h>
但是這兩種形式是有區(qū)別的:使用尖括號(hào)表示在包含文件目錄中去查找(包含
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 印刷服務(wù)合同
- 手破碎機(jī)設(shè)備買(mǎi)賣(mài)合同
- 聘用出納合同增加多場(chǎng)景
- 生產(chǎn)車(chē)間承包合同協(xié)議
- 機(jī)械工程勞務(wù)分包合同
- 河北化工醫(yī)藥職業(yè)技術(shù)學(xué)院《中學(xué)生物課堂教學(xué)技能訓(xùn)練》2023-2024學(xué)年第二學(xué)期期末試卷
- 廣州華立科技職業(yè)學(xué)院《數(shù)據(jù)挖掘與決策管理》2023-2024學(xué)年第二學(xué)期期末試卷
- 寧波衛(wèi)生職業(yè)技術(shù)學(xué)院《界面化學(xué)》2023-2024學(xué)年第二學(xué)期期末試卷
- 宜春學(xué)院《需求工程》2023-2024學(xué)年第二學(xué)期期末試卷
- 宿州職業(yè)技術(shù)學(xué)院《水質(zhì)工程學(xué)實(shí)驗(yàn)》2023-2024學(xué)年第二學(xué)期期末試卷
- 事前績(jī)效評(píng)估具體工作實(shí)施方案
- 六年級(jí)下冊(cè)語(yǔ)文第一單元測(cè)試卷 部編版(含答案)
- 2024年湖南高速鐵路職業(yè)技術(shù)學(xué)院?jiǎn)握新殬I(yè)適應(yīng)性測(cè)試題庫(kù)新版
- 《研學(xué)旅行市場(chǎng)營(yíng)銷(xiāo)》課件-研學(xué)旅行市場(chǎng)營(yíng)銷(xiāo)之社群營(yíng)銷(xiāo)
- clsim100-32藥敏試驗(yàn)標(biāo)準(zhǔn)2023中文版
- LNG加氣站質(zhì)量管理手冊(cè)
- 艱難梭菌感染動(dòng)物模型的建立及其應(yīng)用評(píng)價(jià)
- (正式版)HGT 22820-2024 化工安全儀表系統(tǒng)工程設(shè)計(jì)規(guī)范
- 2024年公安部直屬事業(yè)單位招聘筆試參考題庫(kù)附帶答案詳解
- 《旅游景點(diǎn)云南》課件2
- 《肺癌課件:基本概念與臨床表現(xiàn)》
評(píng)論
0/150
提交評(píng)論