




版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第8章文件8.1問(wèn)題的引入 8.2文件的概念 8.3內(nèi)存和外存的數(shù)據(jù)交流8.4程序如何操作文件8.5關(guān)于文件讀寫(xiě)的討論8.6程序調(diào)試與數(shù)據(jù)測(cè)試文件8.7本章小結(jié)
【主要內(nèi)容】
?文件的概念;
?通過(guò)人工操作文件與程序操作文件的對(duì)比,說(shuō)明文件操作的基本步驟;
?文件操作庫(kù)函數(shù)的介紹及使用方法實(shí)例;
?分類(lèi)簡(jiǎn)要介紹可以對(duì)文件進(jìn)行操作的庫(kù)函數(shù)功能。
【學(xué)習(xí)目標(biāo)】
?能夠創(chuàng)建、讀取、寫(xiě)入、更新文件;
?熟悉順序訪問(wèn)文件處理;
?熟悉隨機(jī)訪問(wèn)文件處理。
編程的目的是對(duì)數(shù)據(jù)進(jìn)行處理,完成特定的功能。數(shù)據(jù)的處理包括數(shù)據(jù)的輸入、加工處理及結(jié)果的輸出。對(duì)程序的運(yùn)行及測(cè)試涉及數(shù)據(jù)的輸入/輸出。數(shù)據(jù)的輸入/輸出有如下特點(diǎn):8.1問(wèn)?題?的?引?入
(1)待處理的數(shù)據(jù)由程序員編程時(shí)在程序中設(shè)定(該程序只能處理固定的數(shù)據(jù))或在程序運(yùn)行時(shí)由用戶(hù)輸入(每次運(yùn)行時(shí)都要重新輸入)。
(2)程序處理的結(jié)果輸出到顯示屏,無(wú)法實(shí)現(xiàn)永久性的保存。
當(dāng)處理的數(shù)據(jù)有下列情形出現(xiàn)時(shí),可把這些數(shù)據(jù)保存起來(lái),以達(dá)到查看方便或可以反復(fù)使用的目的。
(1)輸入:輸入的數(shù)據(jù)量很大;輸入的數(shù)據(jù)每次都相同。
(2)輸出:需要多次查看程序結(jié)果;程序結(jié)果較多,一屏顯示不下。計(jì)算機(jī)系統(tǒng)長(zhǎng)久保存數(shù)據(jù)的方法是把數(shù)據(jù)存儲(chǔ)到外存上,操作系統(tǒng)以文件為單位對(duì)外存的數(shù)據(jù)進(jìn)行管理。
文件:存儲(chǔ)在外部介質(zhì)上具有名稱(chēng)(文件名)的一組相關(guān)數(shù)據(jù)的集合。
文件是一組相關(guān)數(shù)據(jù)的有序集合,這個(gè)數(shù)據(jù)集有一個(gè)名稱(chēng),叫做文件名。實(shí)際上在前面的各章中我們已經(jīng)多次使用了文件,例如源程序文件、目標(biāo)文件、可執(zhí)行文件、庫(kù)文件(頭文件)等。用文件可長(zhǎng)期保存數(shù)據(jù),并實(shí)現(xiàn)數(shù)據(jù)共享。8.2文?件?的?概?念在C語(yǔ)言中按內(nèi)容存放方式,文件可分為二進(jìn)制文件和文本文件兩種。
1.二進(jìn)制文件
二進(jìn)制文件是按二進(jìn)制的編碼方式來(lái)存放數(shù)據(jù)的。例如,整數(shù)5678的存儲(chǔ)形式為0001011000101110,只占兩個(gè)字節(jié)(5678的十六進(jìn)制為0x162E)。
二進(jìn)制文件雖然也可在屏幕上顯示,但其內(nèi)容無(wú)法讀懂。
2.文本文件
文本文件也稱(chēng)為ASCII碼文件,這種文件在磁盤(pán)中存放時(shí)每個(gè)字符對(duì)應(yīng)一個(gè)字節(jié),用于存放對(duì)應(yīng)的ASCII碼。例如,數(shù)5678的存儲(chǔ)形式如表8.1所示。
表8.1文本文件中的字符表示
ASCII碼文件可在屏幕上按字符顯示。例如源程序文件就是ASCII碼文件,由于是按字符顯示,因此我們能讀懂文件內(nèi)容。
文本文件與二進(jìn)制文件的區(qū)別:將文件看做是由一個(gè)一個(gè)字符組成的,一個(gè)字符為一個(gè)單位;而二進(jìn)制文件則是由bit(位)組成的,一個(gè)bit為一個(gè)單位。
流式文件:C語(yǔ)言將文件看做“數(shù)據(jù)流”,即文件是由一串連續(xù)的、無(wú)間隔的字節(jié)構(gòu)
成,用文件結(jié)束符結(jié)束,這種結(jié)構(gòu)稱(chēng)為“流式文件結(jié)構(gòu)”。
流式文件在處理時(shí)不需考慮文件中數(shù)據(jù)的性質(zhì)、類(lèi)型和存放格式,訪問(wèn)時(shí)只是以字節(jié)為單位對(duì)數(shù)據(jù)進(jìn)行存取,而將對(duì)數(shù)據(jù)結(jié)構(gòu)的分析、處理等工作都交給后續(xù)程序去完成。因此,這樣的文件結(jié)構(gòu)更具靈活性,對(duì)存儲(chǔ)空間的利用率高。
文件結(jié)束標(biāo)志有以下兩種:
(1)EOF——文本文件結(jié)束標(biāo)志。EOF是EndofFile的縮寫(xiě),整型符號(hào)常量,在<stdio.h>頭文件中定義,它的值通常是-1。
(2)feof函數(shù)——用來(lái)判斷文件是否結(jié)束。二進(jìn)制文件與文本文件均適用。
關(guān)于EOF
在程序中測(cè)試符號(hào)常量EOF,而不是測(cè)試-1,這可以使程序更具有可移植性。ANSI標(biāo)準(zhǔn)強(qiáng)調(diào),EOF是負(fù)的整型值(但沒(méi)有必要一定是-1)。因此,在不同的系統(tǒng)中,EOF可能具有不同的值,即輸入EOF的按鍵組合取決于系統(tǒng),如下表所示。
緩沖文件系統(tǒng)
由于CPU與內(nèi)存的工作速度非???,而對(duì)外存(磁盤(pán)、光盤(pán)等)的存取速度很慢,當(dāng)訪問(wèn)外存時(shí),主機(jī)必須等待慢速的外存操作完成后才能繼續(xù)工作,嚴(yán)重影響了CPU效率的發(fā)揮。解決二者速度不匹配的方法是采用“緩沖區(qū)”技術(shù)。
緩沖讀寫(xiě)操作可使磁盤(pán)得到高效利用。標(biāo)準(zhǔn)C采用緩沖文件系統(tǒng),如圖8.1所示。8.3內(nèi)存和外存的數(shù)據(jù)交流
圖8.1緩沖文件系統(tǒng)
緩沖區(qū)是在內(nèi)存中分配的一塊存儲(chǔ)空間,是由操作系統(tǒng)在每個(gè)文件被打開(kāi)時(shí)自動(dòng)建立并管理的。緩沖區(qū)的大小由C的具體版本確定,一般為512字節(jié)。
緩沖區(qū)的作用:當(dāng)需要向外存文件中寫(xiě)入數(shù)據(jù)時(shí),并不是每次都直接寫(xiě)入外存,而是先寫(xiě)入到緩沖區(qū),只有當(dāng)緩沖區(qū)的數(shù)據(jù)存滿或文件關(guān)閉時(shí),才自動(dòng)將緩沖區(qū)的數(shù)據(jù)一次性寫(xiě)入外存。讀數(shù)時(shí),也是一次將一個(gè)數(shù)據(jù)塊讀入緩沖區(qū)中,以后讀取數(shù)據(jù)時(shí),先到緩沖區(qū)中尋找,若找到,則直接讀出,否則,再到外存中尋找,找到后將其所在的數(shù)據(jù)塊一次讀入緩沖區(qū)。緩沖區(qū)可有效減少訪問(wèn)外存的次數(shù)。
使用緩沖文件系統(tǒng)時(shí),系統(tǒng)將自動(dòng)為每一個(gè)打開(kāi)的文件建立緩沖區(qū),此后,程序?qū)ξ募淖x寫(xiě)操作實(shí)際上是對(duì)文件緩沖區(qū)的操作。
為了便于編程,ANSIC將有關(guān)文件緩沖區(qū)的一些信息(如緩沖區(qū)對(duì)應(yīng)的文件名、文件所允許的操作方式、緩沖區(qū)的大小以及當(dāng)前讀寫(xiě)數(shù)據(jù)在緩沖區(qū)的位置等)用一個(gè)結(jié)構(gòu)體類(lèi)型來(lái)描述,類(lèi)型名為FILE,該結(jié)構(gòu)體類(lèi)型的定義包含在stdio.h文件中。文件類(lèi)型FILE描述文件緩沖區(qū)的信息,具體內(nèi)容為:
typedefstruct_iobuf
{
char*_ptr; //指向buffer中第一個(gè)未讀的字節(jié)
int_cnt; //記錄剩余未讀字節(jié)的個(gè)數(shù)
char*_base; //指向一個(gè)字符數(shù)組,即這個(gè)文件的緩沖區(qū)
int_flag; //標(biāo)志位,記錄了FILE結(jié)構(gòu)所代表的打開(kāi)文件的一些屬性
int_file; //用于獲取文件描述,可使用fileno函數(shù)獲得此文件的句柄
int_charbuf; //單字節(jié)的緩沖,如果為單字節(jié)緩沖,_base將無(wú)效
int_bufsiz; //緩沖區(qū)大小
char*_tmpfname; //臨時(shí)文件名
}FILE;有了FILE類(lèi)型后,每當(dāng)打開(kāi)一個(gè)文件時(shí),操作系統(tǒng)自動(dòng)為該文件建立一個(gè)FILE類(lèi)型的結(jié)構(gòu)體數(shù)據(jù),并返回指向它的指針,系統(tǒng)將被打開(kāi)文件及緩沖區(qū)的各種信息都存入這個(gè)FILE型數(shù)據(jù)區(qū)域中,程序通過(guò)上述指針獲得文件信息及訪問(wèn)文件,如圖8.2所示。
文件關(guān)閉后,它的文件結(jié)構(gòu)體被釋放。
只要有了指向某個(gè)文件的文件指針,具體的文件操作都由系統(tǒng)提供的文件操作函數(shù)實(shí)現(xiàn),而不需了解文件緩沖區(qū)的具體情況,方便了程序員關(guān)于文件操作的編程。
圖8.2文件操作
文件通常是駐留在外部介質(zhì)(如磁盤(pán)等)上的,在使用時(shí)才調(diào)入內(nèi)存中。
在文件的操作中,經(jīng)常會(huì)涉及文件的讀寫(xiě)操作,通常把數(shù)據(jù)從磁盤(pán)流到內(nèi)存稱(chēng)為“讀”,數(shù)據(jù)從內(nèi)存流到磁盤(pán)稱(chēng)為“寫(xiě)”。
每個(gè)文件由唯一的文件名來(lái)標(biāo)識(shí)。計(jì)算機(jī)按文件名對(duì)文件進(jìn)行讀、寫(xiě)等有關(guān)操作。8.4程序如何操作文件對(duì)文件操作的經(jīng)驗(yàn)是,如果想找存在外部介質(zhì)上的數(shù)據(jù),必須先按文件名找到指定的文件,然后再?gòu)脑撐募凶x取數(shù)據(jù),文件使用完畢,再關(guān)閉它。那么用程序來(lái)對(duì)文件進(jìn)行操作,是否也是這樣的步驟呢?具體又是怎么處理的呢?用程序來(lái)訪問(wèn)文件,與我們直接對(duì)文件的操作與步驟是類(lèi)似的。
程序訪問(wèn)文件的三個(gè)步驟如下:
(1)打開(kāi)文件。
(2)操作文件。
(3)關(guān)閉文件。程序?qū)ξ募牟僮鞑襟E如下:
(1)在磁盤(pán)上建立、保存文件。
(2)打開(kāi)已有文件。
(3)讀寫(xiě)文件。
在C語(yǔ)言中,沒(méi)有輸入/輸出語(yǔ)句,對(duì)文件的讀寫(xiě)都是用庫(kù)函數(shù)來(lái)實(shí)現(xiàn)的。ANSI規(guī)定了標(biāo)準(zhǔn)輸入/輸出函數(shù),用它們對(duì)文件進(jìn)行讀寫(xiě),相應(yīng)的庫(kù)函數(shù)參見(jiàn)附錄C。
廣義上,操作系統(tǒng)將每一個(gè)與主機(jī)相連的輸入/輸出設(shè)備都看做是文件,把它們的輸入/輸出等同于對(duì)磁盤(pán)文件的讀和寫(xiě)。通常把顯示器定義為標(biāo)準(zhǔn)輸出文件,一般情況下在屏幕上顯示有關(guān)信息就是向標(biāo)準(zhǔn)輸出文件輸出。如前面經(jīng)常使用的printf、putchar函數(shù)就是這類(lèi)輸出。
鍵盤(pán)通常被指定為標(biāo)準(zhǔn)輸入文件,從鍵盤(pán)上輸入就意味著從標(biāo)準(zhǔn)輸入文件上輸入數(shù)據(jù)。如scanf、getchar函數(shù)就屬于這類(lèi)輸入。8.4.1打開(kāi)文件
打開(kāi)文件的庫(kù)函數(shù)是fopen。
聲明形式:FILEfopen(char*filename,char*mode)
函數(shù)功能:在內(nèi)存中為文件分配一個(gè)文件緩沖區(qū)。
參數(shù)說(shuō)明:
Filename——字符串,包含欲打開(kāi)的文件路徑及文件名;
Mode——字符串,說(shuō)明打開(kāi)文件的模式。
返回值:文件指針(NULL為異常,表示文件未打開(kāi))。
特別提示:文件打開(kāi)后,應(yīng)檢查此操作是否成功,即判斷文件指針是否為空(NULL),然后才能決定能否對(duì)文件繼續(xù)訪問(wèn)。
表8.2文件打開(kāi)模式說(shuō)明:
(1)打開(kāi)的文件分文本文件與二進(jìn)制文件。
(2)文本文件用“t”表示(可省略);二進(jìn)制文件用“b”表示。
在用戶(hù)希望保存原有文件內(nèi)容時(shí),使用模式“w”來(lái)打開(kāi)文件,使得文件的內(nèi)容丟失而沒(méi)有任何警告。
用不正確的文件模式來(lái)打開(kāi)文件將導(dǎo)致破壞性的錯(cuò)誤。例如,當(dāng)應(yīng)該用更新模式“r+”的時(shí)候用寫(xiě)入模式“w”打開(kāi)文件將刪除文件內(nèi)容。
文件的路徑
用戶(hù)在磁盤(pán)上尋找文件時(shí),所歷經(jīng)的文件夾線路叫路徑。路徑分為絕對(duì)路徑和相對(duì)路徑。絕對(duì)路徑是完整的描述文件位置的路徑,它是從盤(pán)符開(kāi)始的路徑。相對(duì)路徑是相對(duì)于目標(biāo)位置的路徑,是指在當(dāng)前的目錄下開(kāi)始的路徑。
能唯一標(biāo)識(shí)某個(gè)磁盤(pán)文件的字符串形式為:
盤(pán)符:\路徑\文件名.擴(kuò)展名例1:我們要找c:\windows\system\config文件,如果當(dāng)前在c:\winodws\,則相對(duì)路徑表示為system\config,絕對(duì)路徑表示為c:\windows\system\config。
例2:
fp=fopen("a1.txt","r");
表示相對(duì)路徑,無(wú)路徑信息,則a1.txt文件在當(dāng)前目錄下(注:此時(shí)當(dāng)前目錄為程序所在工程的目錄)。
fp=fopen("d:\\qyc\\a1.txt","r")
表示絕對(duì)路徑,a1.txt在d盤(pán)qyc目錄下。
注:此處用“\\”是因?yàn)樵谧址小癨”是要用轉(zhuǎn)義字符表示的。8.4.2關(guān)閉文件
關(guān)閉文件的庫(kù)函數(shù)是fclose。
聲明形式:intfclose(FILE*fp);
函數(shù)功能:關(guān)閉文件指針指向的文件,將緩沖區(qū)數(shù)據(jù)做相應(yīng)處理后釋放緩沖區(qū)。
輸出:如果關(guān)閉文件出錯(cuò),函數(shù)返回非零值;否則返回0。
特別提示:使用完文件后應(yīng)及時(shí)關(guān)閉,否則可能會(huì)丟失數(shù)據(jù),因?yàn)閷?xiě)文件時(shí),只有當(dāng)緩沖區(qū)滿時(shí)才將數(shù)據(jù)真正寫(xiě)入文件,若當(dāng)緩沖區(qū)未滿時(shí)結(jié)束程序運(yùn)行,緩沖區(qū)中的數(shù)據(jù)將會(huì)丟失。
【例8-1】文件的例子1。
1 /*對(duì)data.txt文件寫(xiě)入10條記錄*/
2 #include<stdio.h>
3 intmain()
4 {
5 FILE*fp;/*FILE為文件類(lèi)型*/
6 inti;
7 intx;
8
9 fp=fopen("data.txt","w"); /*以文本寫(xiě)方式"w"打開(kāi)data.txt*/
10
11 for(i=1;i<=10;i++)
12 {
13 scanf("%d",&x);
14 fprintf(fp,"%d",x); /*將x輸出到fp指向的文件中*/
15 }
16 fclose(fp); /*關(guān)閉文件*/
17 return0;
18 }程序結(jié)果:程序運(yùn)行結(jié)束,在程序文件所在工程的目錄下,可以找到新建的文件data.txt,打開(kāi)后即可看到程序運(yùn)行時(shí)從鍵盤(pán)輸入的10個(gè)數(shù)據(jù)。
文件存儲(chǔ)成為二進(jìn)制還是文本文件取決于fopen的方式。如果用wt,則存儲(chǔ)為文本文件,這樣用記事本打開(kāi)就可以正常顯示了;如果用wb,則存儲(chǔ)為二進(jìn)制文件,這樣用記事本打開(kāi)有可能會(huì)出現(xiàn)小方框,若要正常顯示,可以用寫(xiě)字板或UltraEdit等工具打開(kāi)。8.4.3文件的讀寫(xiě)
對(duì)文件的讀寫(xiě)有系列函數(shù),參見(jiàn)表8.3和表8.4。
表8.3文件讀寫(xiě)函數(shù)(1)
表8.4文件讀寫(xiě)函數(shù)(2)特別提示:文件的讀寫(xiě)都是在文件的當(dāng)前位置進(jìn)行的。所謂當(dāng)前位置,是指文件的數(shù)據(jù)讀寫(xiě)指針在當(dāng)前時(shí)刻指示的位置。文件打開(kāi)時(shí),該指針指向文件的開(kāi)頭;一次讀寫(xiě)完成后,該指針自動(dòng)后移(移至本次讀寫(xiě)數(shù)據(jù)的下一個(gè)字節(jié))。
【例8-2】文件的例子2。逐個(gè)按序讀出并顯示已有文件file.txt中的字符。
1 /*按序逐個(gè)讀出文件中的字符*/
2 #include<stdio.h>
3 #include<stdlib.h>
4
5 voidmain()
6 {
7 charch;
8 FILE*fp; /*定義一個(gè)文件類(lèi)型的指針變量fp*/
9 fp=fopen("file.txt","r"); /*以只讀方式打開(kāi)文本文件file.Txt*/
10 if(fp==NULL) /*打開(kāi)文件失敗*/
11 {
12 printf("cannotopenthisfile\n");
13 exit(0); /*庫(kù)函數(shù)exit,終止程序*/
14 }
15 ch=fgetc(fp); /*讀出文件中的一個(gè)字符,賦給變量ch*/
16 while(ch!=EOF) /*判斷文件是否結(jié)束,此判斷條件等價(jià)于(!feof(fp))*/
17 {
18 putchar(ch); /*輸出從文件中讀出的字符*/
19 ch=fgetc(fp); /*讀出文件中的一個(gè)字符,賦給變量ch*/
20 }
21 fclose(fp); /*關(guān)閉文件*/
22 return0;
23 }
exit函數(shù):在<stdlib.h>中聲明,將強(qiáng)制程序結(jié)束。在檢測(cè)到輸入錯(cuò)誤或者程序無(wú)法打開(kāi)要處理的文件時(shí)使用。
exit(0)為正常退出,exit(1),為非正常退出(只要其中的參數(shù)不為零)。
C語(yǔ)言中的exit()和return有什么不同?
在主程序main中,語(yǔ)句return(表達(dá)式)等價(jià)于函數(shù)exit(表達(dá)式)。但是,函數(shù)exit有一個(gè)優(yōu)點(diǎn),它可以從其他函數(shù)中調(diào)用,并且可以用查找程序查找這些調(diào)用。用exit()函數(shù)可以退出程序并將控制權(quán)返回給操作系統(tǒng),而用return語(yǔ)句可以從一個(gè)函數(shù)中返回并將控制權(quán)返回給調(diào)用該函數(shù)的函數(shù)。如果在main()函數(shù)中加入return語(yǔ)句,那么在執(zhí)行這條語(yǔ)句后將退出main()函數(shù)并將控制權(quán)返回給操作系統(tǒng),這樣的一條return語(yǔ)句和exit()函數(shù)的作用是相同的。
【例8-3】文件的例子3。將指定字符串寫(xiě)到文件中;將文件中的字符串讀到數(shù)
組里。
1 /*將指定字符串寫(xiě)到文件中*/
2 #include<stdio.h>
3 chars="Iamastudent"; /*設(shè)定字符串s*/
4 intmain()
5 {
6 chara[100];
7 FILE*fp; /*定義文件指針為fp*/
8 intn=strlen(s); /*計(jì)算字符串s的長(zhǎng)度*/
9
10 /*以寫(xiě)方式打開(kāi)文本文件f1.txt*/
11 if((fp=fopen("f1.txt","w"))!=NULL)
12 {
13 fputs(s,fp); /*將s所指的字符串寫(xiě)到fp所指的文件中*/
14 }
15 fclose(fp); /*關(guān)閉fp所指向的文件*/
16
17 /*以只讀方式打開(kāi)文本文件f1.txt*/
18 fp=fopen("f1.txt","r");
19 fgets(a,n+1,fp); /*將fp所指的文件中的內(nèi)容讀到a中*/
20 printf("%s\n",a); /*輸出a中的內(nèi)容*/
21 fclose(fp); /*關(guān)閉fp所指向的文件*/
22 return0;
23 }說(shuō)明:第19行語(yǔ)句fgets(a,n+1,fp)是將讀出的字符串放入a串中,其中a是已經(jīng)定義好的字符串,n+1是讓fp所指的文件內(nèi)容依次取n個(gè)字符給a,這n個(gè)字符恰為s串的內(nèi)容,之后還要在該串后自動(dòng)加入一個(gè)'\0'字符,因此要寫(xiě)n+1。
【例8-4】文件的例子4。向磁盤(pán)寫(xiě)入格式化數(shù)據(jù),再?gòu)脑撐募x出顯示到屏幕。
1 /*數(shù)據(jù)成塊寫(xiě)入文件*/
2 #include"stdio.h"
3 #include"stdlib.h"
4
5 intmain()
6 {
7 FILE*fp1;
8 inti;
9 structstudent/*定義結(jié)構(gòu)體*/
10 {
11 charname[15];
12 charnum[6];
13 floatscore[2];
14 }stu;
15
16 fp1=fopen("test.txt","wb");
17 if(fp1==NULL) /*以二進(jìn)制只寫(xiě)方式打開(kāi)文件*/
18 {
19 printf("cannotopenfile");
20 exit(0);
21 }
22 printf("inputdata:\n");
23 for(i=0;i<2;i++)
24 {
25 /*輸入一行記錄*/
26 scanf("%s%s%f%f",
27 ,stu.num,&stu.score[0],&stu.score[1]);
28 /*成塊寫(xiě)入文件,一次寫(xiě)結(jié)構(gòu)的一行*/
29 fwrite(&stu,sizeof(stu),1,fp1);
30 }
31 fclose(fp1);
32
33 /*重新以二進(jìn)制只寫(xiě)方式打開(kāi)文件*/
34 if((fp1=fopen("test.txt","rb"))==NULL)
35 {
36 printf("cannotopenfile");
37 exit(0);
38 }
39 printf("outputfromfile:\n");
40 for(i=0;i<2;i++)
41 {
42 fread(&stu,sizeof(stu),1,fp1); /*從文件成塊讀*/
43 printf("%s%s%7.2f%7.2f\n", /*顯示到屏幕*/
44 ,stu.num,stu.score[0],stu.score[1]);
45 }
46 fclose(fp1);
47 return0;
48 }
程序結(jié)果:
inputdata:
xiaowangj00187.598.4
xiaolij00299.589.6
outputfromfile:
xiaowangj00187.5098.40
xiaolij00299.5089.60
把希望的內(nèi)容寫(xiě)入文件后,查看文件時(shí)出現(xiàn)亂碼,這往往是文件操作函數(shù)要求的文件制式與你寫(xiě)入時(shí)打開(kāi)文件的制式不一致造成的。8.4.4文件位置的確定
確定文件位置的庫(kù)函數(shù)是fseek。
聲明形式:fseek(文件類(lèi)型指針,位移量,起始點(diǎn)位置)
函數(shù)功能:重定位文件內(nèi)部指針的位置。以“起始點(diǎn)位置”為基準(zhǔn),按“位移量”指定字節(jié)數(shù)做偏移(起始位置值:文件頭0,當(dāng)前位置1,文件尾2)。
返回值:成功返回0;失敗返回-1。
【例8-5】文件的例子5。已知stu_list.txt中存放了多個(gè)學(xué)生的信息,在此文件中讀出第二個(gè)學(xué)生的數(shù)據(jù)。
程序?qū)崿F(xiàn):
1 /*在文件指定位置讀取數(shù)據(jù)——對(duì)文件進(jìn)行隨機(jī)讀寫(xiě)*/
2 #include"stdio.h"
3 #include"stdlib.h"
4
5 structstu/*學(xué)生信息結(jié)構(gòu)*/
6 {
7 charname[10];
8 intnum;
9 intage;
10 charaddr[15];
11 }boy,*qPtr;/*定義結(jié)構(gòu)變量boy,結(jié)構(gòu)指針qPtr*/
12
13 intmain()
14 {
15 FILE*fp;
16 charch;
17 inti=1;/*跳過(guò)結(jié)構(gòu)的前i行*/
18 qPtr=&boy;/*qPtr指向boy結(jié)構(gòu)體的起始位置*/
19
20 if((fp=fopen("stu_list.txt","rb"))==NULL)
21 {
22 printf("Cannotopenfile!");
23 exit(0);
24 }
25 /*使文件的位置指針重新定位于文件開(kāi)頭*/
26 rewind(fp);
27 /*從文件頭開(kāi)始,向后移動(dòng)i個(gè)結(jié)構(gòu)大小的字節(jié)數(shù)*/
28 fseek(fp,i*sizeof(structstu),0);
29 /*從fp文件中讀出結(jié)構(gòu)的當(dāng)前行,放到qPtr指向的地址中*/
30 fread(qPtr,sizeof(structstu),1,fp);
31 printf("%st%5d%7d%sn",qPtr->name,
32 qPtr->num,qPtr->age,qPtr->addr);
33 return0;
34 }
(1)有的教材中提到:按文本方式或二進(jìn)制方式中的某一種方式存儲(chǔ)的文件,使用時(shí)必須以原來(lái)的方式從外存中讀出,才能保證數(shù)據(jù)的正確性。
(2)程序設(shè)計(jì)錯(cuò)誤:把希望的內(nèi)容寫(xiě)入文件后,查看文件時(shí)出現(xiàn)亂碼,這往往是文件操作函數(shù)要求的文件制式與寫(xiě)入文件時(shí)打開(kāi)文件的制式不一致造成的。8.5關(guān)于文件讀寫(xiě)的討論
這究竟是怎么回事呢?
以下按不同的情形來(lái)討論,要點(diǎn):
(1)在不同文件打開(kāi)模式(文本、二進(jìn)制)狀態(tài)下,文件緩沖區(qū)中的數(shù)據(jù)是否正確。
(2)在不同文件打開(kāi)模式(文本、二進(jìn)制)狀態(tài)下,寫(xiě)入文件中的數(shù)據(jù)是否顯示正常。
【情形一】以fprintf方式向文件data.txt寫(xiě)入數(shù)據(jù),以fscanf方式讀出data.txt中的數(shù)據(jù)。
1 /*文件的讀寫(xiě)方式*/
2 #include<stdio.h>
3 #include<stdlib.h>
4
5 intmain()
6 {
7 FILE*fp;/*FILE為文件類(lèi)型*/
8 inti;
9 intx;
10 intb=0;
11
12 fp=fopen("data.txt","wb");/*以"wb"方式打開(kāi)data.txt文件*/
13
14 if(fp==NULL) /*打開(kāi)文件失敗*/
15 {
16 printf("1:cannotopenthisfile\n");
17 exit(0); /*庫(kù)函數(shù)exit終止程序*/
18 }
19 /****以fprintf格式寫(xiě)入數(shù)據(jù)*****/
20 for(i=1;i<7;i++)
21 {
22 scanf("%d",&x);
23 fprintf(fp,"%d\n",x);/*將x輸出到fp指向的文件中*/
24 }
25 fclose(fp);/*關(guān)閉文件*/
26
27 fp=fopen("data.txt","r"); /*以只讀方式打開(kāi)文本文件data.Txt*/
28 if(fp==NULL) /*打開(kāi)文件失敗*/
29 {
30 printf("2:cannotopenthisfile\n");
31 exit(0); /*庫(kù)函數(shù)exit終止程序*/
32 }
33
34 /*********以fscanf方式從文件中讀出數(shù)據(jù)*********/
35 fscanf(fp,"%d",&x); /*讀出文件中的一個(gè)int型數(shù)值給x*/
36 while(!feof(fp)) /*判斷文件是否結(jié)束*/
37 {
38 printf("%d",x);
39 fscanf(fp,"%d",&x);
40 }
41 fclose(fp);/*關(guān)閉文件*/
42 return0;
43 }
程序結(jié)果:
輸入:234567
輸出:234567
【情形二】以fprintf方式向data.txt文件寫(xiě)入數(shù)據(jù),以fgetc方式讀出data.txt中的數(shù)據(jù)。
分別用以下灰色框中的語(yǔ)句替代情形一中兩個(gè)灰色框的語(yǔ)句。
/****以fprintf格式寫(xiě)入數(shù)據(jù)*****/
for(i=1;i<7;i++)
{
scanf("%d",&x);
fprintf(fp,"%d\n",x); /*將x輸出到fp指向的文件中*/
}
/*********以fgetc方式從文件中讀出的字符*********/
ch=fgetc(fp); /*讀出文件中的一個(gè)字符,賦給變量ch*/
while(ch!=EOF) /*判斷文件是否結(jié)束,此判斷條件等價(jià)于(!feof(fp))*/
{
putchar(ch); /*輸出從文件中讀出的字符*/
ch=fgetc(fp); /*讀出文件中的一個(gè)字符,賦給變量ch*/
}
程序結(jié)果:
輸入:
234567
輸出:
2
3
4
5
6
7
【情形三】以fwrite方式向文件data.txt寫(xiě)入數(shù)據(jù),以fread方式讀出data.txt中的數(shù)據(jù)。
分別用以下灰色框中的語(yǔ)句替代情形一中兩個(gè)灰色框的語(yǔ)句。
/****以fwrite格式寫(xiě)入數(shù)據(jù)*****/
for(i=1;i<7;i++) /*循環(huán)6次,把6個(gè)int型數(shù)據(jù)寫(xiě)入文件*/
{
scanf("%d",&x);
fwrite(&x,sizeof(int),1,fp);/*將x輸出到fp指向的文件中*/
}
/****以fread格式讀出數(shù)據(jù)*****/
for(i=1;i<7;i++)
{
fread(&b,sizeof(int),1,fp);
printf("b=%x\n",b);
}
程序結(jié)果:
輸入:
234567
輸出:
b=2
b=3
b=4
b=5
b=6
b=7
查看文件緩沖區(qū)。圖8.3~圖8.8是分別為情形三的調(diào)試步驟1~調(diào)試步驟6。
圖8.3中,創(chuàng)建文件,文件指針fp為0x426af8。
圖8.4中,緩沖區(qū)_base地址是0x3851f0。
圖8.3情形三調(diào)試步驟1
圖8.4情形三調(diào)試步驟2圖8.5中,緩沖區(qū)_bass中的內(nèi)容為6個(gè)輸入的int數(shù)據(jù),一個(gè)int占4
byte。
圖8.6中,以讀方式打開(kāi)文件,fp指針為0x426af8,同前面建立文件時(shí)是一樣的。注意緩沖區(qū)_base地址是0x3851f0,即同前面寫(xiě)文件時(shí)也是一樣的。
_ptr(指向buffer中第一個(gè)未讀的字節(jié))指向的是0x3851f4,將要讀出的字節(jié)值為3。
圖8.5情形三調(diào)試步驟3圖8.6情形三調(diào)試步驟4
讀與寫(xiě)用的是同一緩沖區(qū)。
圖8.7中,_ptr指向的是0x3851f8,將要讀出的字節(jié)值為4。
圖8.8中,緩沖區(qū)_base地址是0x3851f0,其中的內(nèi)容也依然未變。
圖8.7情形三調(diào)試步驟5
圖8.8調(diào)試步驟6
表8.5for循環(huán)相關(guān)量的變化表8.5中:
char*_ptr; /*指向buffer中第一個(gè)未讀的字節(jié)*/
int_cnt; /*記錄剩余未讀字節(jié)的個(gè)數(shù)*/
char*_base; /*指向一個(gè)字符數(shù)組,即這個(gè)文件的緩沖區(qū)*/
【情形四】以fprintf方式向文件data.txt寫(xiě)入數(shù)據(jù),以fread方式讀出data.txt中的數(shù)據(jù)。省略的程序語(yǔ)句同情形一。
/****以fprintf格式寫(xiě)入數(shù)據(jù)*****/
for(i=1;i<7;i++)
{
scanf("%d",&x);
fprintf(fp,"%d\n",x);/*將x輸出到fp指向的文件中*/
}
/**************以fread方式讀出數(shù)據(jù)***************/
for(i=1;i<4;i++)
{
fread(&b,sizeof(int),1,fp);
printf("b=%x\n",b);
}
程序結(jié)果:
輸入:
234567
輸出:
b=a330a32
b=a350a34
b=a370a36
情形一中,以fprintf方式寫(xiě)入數(shù)據(jù),以fscanf方式讀出數(shù)據(jù);情形四中,以fprintf方式寫(xiě)入數(shù)據(jù),以fread方式讀出數(shù)據(jù)。情形一的結(jié)果是正常的,為什么情形四的結(jié)果看起來(lái)就不對(duì)了呢?
答:跟蹤查看一下便知。
圖8.9、圖8.10所示分別為情形四的調(diào)試步驟1和調(diào)試步驟2。
圖8.9緩沖區(qū)的數(shù)據(jù)中,
0x0A是'\n'的ASCII碼,是語(yǔ)句fprintf(fp,"%d\n",x)中的'\n'產(chǎn)生的,同輸入的數(shù)字一起寫(xiě)入到文件中。
圖8.9情形四調(diào)試步驟1圖8.10中,用fread(&b,sizeof(int),1,fp)語(yǔ)句讀緩沖區(qū)中的內(nèi)容時(shí),是按照sizeof(int)等于4
byte的長(zhǎng)度讀出并賦給變量b的。所以i=1時(shí),b的值為從0x3851f0開(kāi)始的4
byte的數(shù)值,即為0x0a330a32,對(duì)應(yīng)十進(jìn)制數(shù)為171117106。圖8.10情形四調(diào)試步驟2總之,無(wú)論文件的寫(xiě)入方式、讀出方式是文本模式還是二進(jìn)制模式,程序結(jié)果都如表8.6所示,即讀寫(xiě)的文件模式不影響程序的結(jié)果。
表8.6讀寫(xiě)方式與程序結(jié)果至于寫(xiě)入data.txt文件的數(shù)據(jù)是否能正常查看,則與讀寫(xiě)的文件模式有關(guān),具體見(jiàn)表8.7。
表8.7data文件的顯示結(jié)果
(1)在文件緩沖區(qū)中的數(shù)據(jù)形式,與文件讀寫(xiě)模式無(wú)關(guān);
(2)程序生成的文件是否能正常顯示,與文件讀寫(xiě)模式有關(guān);
(3)若程序讀文件出現(xiàn)數(shù)據(jù)顯示不對(duì),可以通過(guò)查看緩沖區(qū)數(shù)據(jù)的格式,選擇合適的操作函數(shù)。
當(dāng)我們?cè)谠O(shè)計(jì)好算法和程序后,要在調(diào)試環(huán)境中輸入測(cè)試數(shù)據(jù),查看程序運(yùn)行的結(jié)果。由于調(diào)試往往不能一次成功,每次運(yùn)行時(shí),都要重新輸入一遍測(cè)試數(shù)據(jù),對(duì)于有大量輸入數(shù)據(jù)的題目,直接從鍵盤(pán)輸入數(shù)據(jù)需要花費(fèi)大量時(shí)間。
8.6程序調(diào)試與數(shù)據(jù)測(cè)試文件我們可以把要輸入的數(shù)據(jù)事先放在文件中,用文件的讀函數(shù)讀入;將程序運(yùn)行的結(jié)果用文件寫(xiě)函數(shù)寫(xiě)入指定的文件。可以根據(jù)測(cè)試數(shù)據(jù)的特點(diǎn)選用文件讀寫(xiě)函數(shù)。下面給出兩個(gè)代碼模板。
1.使用fscanf和fprintf函數(shù)
【代碼模板一】
#include<stdio.h>
intmain()
{
FILE*fp1,*fp2;
fp1=fopen("data.in","r"); /*以只讀方式打開(kāi)輸入文件data.in*/
fp2=fopen("data.out","w"); /*以寫(xiě)方式打開(kāi)輸出文件data.out*/
/*中間按原樣寫(xiě)代碼,把scanf改為fscanf,printf改為fprintf即可*/
fclose(fp1);
fclose(fp2);
return0;
}
2.使用freopen函數(shù)
聲明形式:FILE*freopen(constchar*path,constchar*mode,FILE*stream);
參數(shù)說(shuō)明:
path——文件名,用于存儲(chǔ)輸入/輸出的自定義文件名;
mode——文件打開(kāi)的模式,和fopen中的模式(如r為只讀,w為寫(xiě))相同;
stream——一個(gè)文件,通常使用標(biāo)準(zhǔn)流文件。
功能:實(shí)現(xiàn)重定向,把預(yù)定義的標(biāo)準(zhǔn)流文件定向到由path指定的文件中。返回值:成功,返回一個(gè)path所指定文件的指針;失敗,返回NULL。(一般不使用它的返回值。)
標(biāo)準(zhǔn)流文件
啟動(dòng)一個(gè)C語(yǔ)言程序時(shí),操作系統(tǒng)環(huán)境負(fù)責(zé)打開(kāi)3個(gè)文件,并將這3個(gè)文件的指針提供給該程序。這3個(gè)文件指針?lè)謩e為標(biāo)準(zhǔn)輸入stdin、標(biāo)準(zhǔn)輸出stdout和標(biāo)準(zhǔn)錯(cuò)誤stderr。它們?cè)?lt;stdio.h>中聲明,其中:
stdin:標(biāo)準(zhǔn)輸入流,默認(rèn)為鍵盤(pán)輸入;
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 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ì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 房產(chǎn)代持合同協(xié)議書(shū)范本
- 汽車(chē)內(nèi)飾配件采購(gòu)合同
- 離婚后住房分配合同樣本
- 二手施工設(shè)備購(gòu)銷(xiāo)合同
- 家族遺產(chǎn)分配合同
- 借款擔(dān)保反擔(dān)保合同樣本
- 學(xué)校裝修合同案例
- 門(mén)面房屋買(mǎi)賣(mài)合同
- 太陽(yáng)能發(fā)電政策考核試卷
- 新材料在新能源領(lǐng)域的應(yīng)用考核試卷
- 中央2025年中國(guó)科協(xié)所屬單位招聘社會(huì)在職人員14人筆試歷年參考題庫(kù)附帶答案詳解-1
- 殯儀服務(wù)員職業(yè)技能鑒定考試題(附答案)
- 電動(dòng)葫蘆吊裝方案計(jì)劃
- 2025年山東電工電氣集團(tuán)招聘筆試參考題庫(kù)含答案解析
- 《建立特種設(shè)備“日管控、周排查、月調(diào)度”工作機(jī)制》專(zhuān)題培訓(xùn)
- 造價(jià)咨詢(xún)服務(wù)方案進(jìn)度計(jì)劃安排及保證措施
- 2024年認(rèn)證行業(yè)法律法規(guī)及認(rèn)證基礎(chǔ)知識(shí) CCAA年度確認(rèn) 試題與答案
- 2024年濰坊工程職業(yè)學(xué)院高職單招(英語(yǔ)/數(shù)學(xué)/語(yǔ)文)筆試歷年參考題庫(kù)含答案解析
- 哈工大微電子工藝緒論01單晶硅
- 供養(yǎng)直系親屬有關(guān)文件
- 穿孔鋁板技術(shù)交底
評(píng)論
0/150
提交評(píng)論