標籤:
http://www.iteedu.com/plang/ccpp/cppdxjch2b/111.php
C++語言把每一個檔案都看成一個有序的位元組流(見圖14.2),每一個檔案或者以檔案結束符(end-of-file marker)結束,或者在特定的位元組號處結束(結束檔案的特定的位元組號記錄在由系統維護和管理的資料結構中)。當開啟一個檔案時,該檔案就和某個流關聯起來。第11章曾介紹過cin、cout、cerr和clog這4個對象會自動產生。與這些對象相關聯的流提供者與特定檔案或裝置之間的通訊通道。例如.cin對象(標準輸入資料流對象)使程式能從鍵盤輸入資料,cout對象(標準輸出資料流對象)使程式能向螢幕輸出資料,cerr和clog對象(標準錯誤流對象)使程式能向螢幕輸出錯誤訊息。
圖14.2 C++把檔案看成n個位元組
要在C++中進行檔案處理,就要包括標頭檔<iostream.h>和<fstream.h>。<fstream.h>標頭檔包括流類ifstream(從檔案輸入)、ofstream(向檔案輸出)和fstream(從檔案輸入,輸出)的定義。產生這些流類的對象即可開啟檔案。這些流類分別從istream、ostream和iostream類派生(即繼承它們的功能)。這樣,第11章“C++輸入,輸出資料流”中介紹的成員函數、運算子和流操縱運算元也可用於檔案流。I/O類的繼承關係見圖14.3。
14.4 建立順序訪問檔案
因為C++把檔案看著是無結構的位元組流,所以記錄等等的說法在C++檔案中是不存在的。為此,程式員必須提供滿足特定應用程式要求的檔案結構。下例說明了程式員是怎樣給檔案強加一個記錄結構。先列出程式,然後再分析細節。
圖14.4中的程式建立了一個簡單的順序訪問檔案,該檔案可用在應收賬目管理系統中跟蹤公司借貸客戶的欠款數目。程式能夠擷取每一個客戶的帳號、客戶名和對客戶的結算額。一個客戶的資料就構成了該客戶的記錄。帳號在應用程式中用作記錄關鍵字,檔案按帳號順序建立和維護。範常式序假定使用者是按帳號順序鍵人記錄的(為了讓使用者按任意順序鍵入記錄,完善的應收賬目管理系統應該具備排序能力)。然後把鍵入的記錄儲存並寫入檔案。
// Fig. 14.4; fig14_04.cpp // Create a sequential file #include <iostream.h> #include <fstream.h> #include <stdlib.h> int main(){ // ofstream constructor opens file ofstream outClientFile( "clients.dat", ios::out ); if ( !outClientFile ) { // overloaded ! operator cerr << "File could not be opened" << endl; exit( 1 ); // prototype in stdlib.h } cout << "Enter the account, name, and balance.\n" << "Enter end-of-file to end input.\n? "; int account; char name[ 30 ]; float balance; while (cin >> account >> name >> balance ) { outClientFile << account << ‘ ‘ << name << ‘ ‘ << balance << ‘\n‘; cout << "? "; } return 0; // ofstream destructor closes file }
輸出結果:Enter the account, name, and balance.Enter end-of-file to end input.? 100 Jones 24.98? 200 Doe 345.67? 300 White 0? 400 Stone -42.16? 500 Rich 224.62? ^z
圖 14.4 建立循序檔
現在我們來研究這個程式。前面曾介紹過,檔案通過建立ifstream、ofstream或fstream流類的對象而開啟。圖14.4中,要開啟檔案以便輸出,因此產生ofstream對象。向物件建構函數傳入兩個參數——檔案名稱和檔案開啟檔案。對於ostream對象,檔案開啟檔案可以是ios::out(將資料輸出到檔案)或ios::app(將資料添加到檔案末尾,而不修改檔案中現有的資料)。現有檔案用ios::out開啟時會截尾,即檔案中的所有資料均刪除。如果指定檔案還不存在,則用該檔案名稱產生這個檔案。下列聲明(第10行):
ofstream outClientFile(“clients.dat”.ios::out);
產生ofstream對象outClientfile,與開啟輸出的檔案clients.dat相關聯。參數"clients.dat"和ios::out傳入ofstream建構函式,該函數開啟檔案,從而建立與檔案的通訊線路。預設情況下,開啟ofstream對象以便輸出,因此下列語句:
ofstream outClientFile (”clients.dat”);
也可以開啟clients.dat進行輸出。圖14.5列出了檔案開啟檔案。
常見編程錯誤14.1
開啟一個使用者想保留資料的現有檔案進行輸出(ios::out方式)。這種操作會刪除檔案的內容而不會給予警告。
常見編程錯誤14. 2
用錯誤的ofstream對象指明一個檔案。
也可以產生ofstream對象而不開啟特定檔案,可以在後面再將檔案與對象相串連。例如,下列聲明:
ofstream outClientFile;
產生以ofstram對象outClientFile。ofstream成員函數open開啟檔案並將其與現有ofstream對象相串連,如下所示:
outClientFile open(”clients.dat”,ios::out);
------------------------------------------------------------------------------------------ 檔案開啟檔案 說明------------------------------------------------------------------------------------------ ios::app 將所有輸出寫入檔案末尾 ios::ate 開啟檔案以便輸出,井移到檔案末尾(通常用於添加資料)資料可以寫入 檔案中的任何地方 ios::in 開啟檔案以便輸入 ios::out 開啟檔案以便輸出 ios::trunc 刪除檔案現有內容(是ios::out的預設操作) ios::nocreate 如果檔案不存在,則檔案開啟失敗 ios::noreplace 如果檔案存在,則檔案開啟失敗------------------------------------------------------------------------------------------
圖14. 5 檔案開啟檔案
常見編程錯誤 14. 3
在引用檔案之前忘記開啟該檔案。
產生ofstream對象並準備開啟時,程式測試開啟操作是否成功。下列if結構中的操作(第12行到第15行):
if ( !outClientFile ) { cerr << "File could not be opened" << endl; exit(1); }
用重載的ios運算子成員函數operator!確定開啟操作是否成功。如果open操作的流將failbit或badbit設定,則這個條件返回非0值(true)。可能的錯誤是試圖開啟讀取不存在的檔案、試圖開啟讀取沒有許可權的檔案或試圖開啟檔案以便寫人而磁碟空間不足。
如果條件表示開啟操作不成功.則輸出錯誤訊息“File could not be opened",並調用函數exit結束程式,exit的參數返回到調用該程式的環境中,參數0表示程式正常終止.任何其他值表示程式因某個錯誤而終止。exit返回的值讓調用環境(通常是作業系統)對錯誤做出相應的響應。
另一個重載的ios運算子成員函數operator void*將流變成指標,使其測試為0(null 指標)或非0(任何其他指標值)。如果failbit或badbit(見第11章)對流進行設定,則返回0(false)。下列while首部的條件自動調用operator void*成員函數:
while (cin >> account >> name >> balance )
只要cin的failbit和badbit都沒有設定,則條件保持true。輸入檔案結束符設定cin的failbit。operator void*函數可以測試輸入對象的檔案結束符,而不必對輸入對象顯式調用eof成員函數。
如果檔案開啟成功,則程式開始處理資料。下列語句(第17行和第18行)提示使用者對每個記錄輸入不同域,或在資料輸入完成時輸入檔案結束符:
cout << "Enter the account, name, and balance. "<< "Enter EOF to and input. ";
圖14. 6列出了不同電腦系統中檔案結束符的鍵盤組合。
--------------------------------------------------- 電腦系統 按鍵組合--------------------------------------------------- UNIX系統 d IBM PC及其相容機 z Macintosh d VAX(VMS) z---------------------------------------------------
圖14.6各種流行的電腦系統中的檔案結束按鍵組合
下列語句(第24行):
while (cin >> account >> name >> balance )
輸入每組資料並確定是否輸人了檔案結束符。輸入檔案結束符或不合法資料時,cin的流讀取運算子 >>返回0(通常這個流讀取運算子>>返回cin),while結構終止。使用者輸入檔案結束符告訴程式沒有更多要處理的資料。當使用者輸入檔案結束符按鍵組合時,設定檔案結束符。只要沒有輸入檔案結束符,while結構就一直迴圈。
第25行和第26行:
outClientFile << account << ‘ ‘ << name<< ‘ ‘ << balance << ‘ ‘;
用流插人運算子<<和程式開頭與檔案相關聯的outClientFile對象將一組資料寫入檔案”clients.dat"。
可以用讀取檔案的程式取得這些資料(見14.5節)。注意圖14.4中產生的檔案是文字檔,可以用任何文字編輯器讀取。
輸人檔案結束符後,main終止,使得outClientFile對象刪除,從而調用其解構函式,關閉檔案
clients.dat。程式員可以用成員函數close顯式關閉ofstream對象,如下所示:
outClientFile.close();
效能提示14. 1
程式不再引用的檔案應立即顯式關閉.這樣可以減少程式繼續執行時佔用的資源。這種方法還可以提高程式的清晰性。
在圖14.4的執行範例中,使用者輸人了五條記錄.然後鍵入了表示資料輸入結束的檔案結束符(IBM PC相容機的螢幕上顯示^z)。輸出結果的交談視窗中沒有說明這些記錄究竟是怎樣在檔案中組織。為了驗證檔案的建立是成功的,下一節介紹了讀取和列印該檔案的程式。
【轉載】C++的檔案和流