IO相關2(文件輸入輸出)
除了繼承自 iostream 類型的行為之外,fstream 中定義的類型還增加了一些新的成員來管理與流相關的文件。我們可以對 fstream,ifstream 和 ofstream 對象調用這些操作,但不能對其他 IO 類型調用這些操作:
1 fstream fstrm;//創建一個未綁定的文件流.fstream是頭文件fstream中定義的一個類型 2 fstream fstrm1(s);//創建一個fstream,並打開名未s的文件.s可以是string類型或者是一個指向c風格字符串的指針.這些構造函數都是explicit的。默認的文件模式mode依賴於fstream的類型 3fstream fstrm2(s, mode);//與前一個構造函數類似,但指定mode打開文件 4 fstrm.open(s);//打開名為s的文件,並將文件與fstrm綁定.s可以是一個string或一個指向c風格字符串的指針.默認的文件mode依賴於fstream的類型.返回void 5 fstrm.close();//關閉fstrm綁定的文件.返回void 6 fstrm.is_open();//返回一個bool值,指出與fstrm關聯的文件是否成功打開且尚未關閉
使用文件流對象:
ifstream in(ifile);//構造一個ifstram並打開給定文件
ofstream out;//輸出文件流未關聯到任何文件
1 #include <iostream> 2 #include <fstream> 3 using namespace std; 4 5 char const *filename = "D:\\code\\c++\\ac27.text"; 6 7 string str; 8 9 int main(void){ 10 ofstream file(filename); 11 if(!file) return 0; 12 file << "hello world!" << endl; 13 file.close();14 15 ifstream out; 16 out.open(filename); 17 while(out >> str){ 18 cout << str << endl; 19 } 20 out.close(); 21 return 0; 22 }
用 fstream 代替 iostream&
在要求使用基類對象的地方,我們可以用繼承類型的對象來替代。因此,如果一個函數接受一個 ostream& 參數,我們在調用這個函數時,可以傳遞給他一個 ofstream 對象,對 istream& 和 ifstream 也是類似的:
1 #include "Sales_data.h" 2 #include <iostream> 3 #include <fstream> 4 using namespace std; 5 6 int main(int argc, char const *argv[]){ 7 ifstream input(argv[1]); 8 ofstream output(argv[2]); 9 Sales_data total; 10 if(read(input, total)){ 11 Sales_data trans; 12 while(read(input, trans)){ 13 if(total.isbn() == trans.isbn()) total += trans; 14 else{ 15 print(output, total) << endl; 16 total = trans; 17 } 18 } 19 print(output, total) << endl; 20 }else cerr << "NO data?" << endl; 21 return 0; 22 }
read 和 print 兩個函數定義時指定的形參分別時 istream& 和 ostream&,但我們可以傳遞 fstream 對象。
自動構造和析構:
考慮這樣一個程序,它的 main 函數接受一個要處理的文件列表:
1 #include <iostream> 2 #include <fstream> 3 using namespace std; 4 5 const string gel("D:\\code\\c++\\"); 6 7 void process(ifstream &input){ 8 string str; 9 input >> str; 10 cout << str << endl; 11 } 12 13 int main(int argc, char const *argv[]){ 14 for(int i = 1; i < argc; i++){ 15 const string filename = (char)(i - 1 + ‘a‘) + (string)".txt"; 16 // cout << filename << endl; 17 ofstream output(gel + filename); 18 output << filename << endl; 19 // output.close();//fstream對象離開其作用域時,與之聯系的文件自動關閉(close會自動調用) 20 } 21 22 //對每個傳遞給程序的文件執行循環操作 23 for(auto p = argv + 1; p != argv + argc; ++p){ 24 ifstream input(*p);//創建輸出流並打開文件 25 if(input) process(input); 26 else cerr << "couldn‘t open: " + string(*p) << endl; 27 // input.close(); 28 } 29 return 0; 30 }
因為 output 和 input 是局部變量,它在每個循環步中都要創建和銷毀一次。當一個 fstream 對象離開其作用域時,與之對應的文件會自動關閉。在下一步循環中,它們會再次被創建。當一個 fstream 對象被銷毀時,close 會自動調用。
文件模式:
每個流都有一個關聯的文件模式,用來指出如何使用文件:
in 以讀方式打開
out 以寫方式打開
app 每次寫操作前均定位到文件末尾
ate 打開文件後立即定位到文件末尾
trunc 截斷文件
binary 以二進制方式進行IO
指定文件模式有如下限制:
1.只可以對 ofstream 或 fstream 對象設定 out 模式。
2.只可以對 ifstream 或 fstream 對象設定 in 模式。
3.只有當 out 也被設定時才可以設定 trunc 模式。
4.只要 trunc 沒被設定,就可以設定 app 模式。在 app 模式下,即使沒有顯示指定 out 模式,文件也總是以輸出方式被打開。
5.默認情況下,即使我們沒有指定 trunc,以 out 模式打開的文件也會被截斷。為了保留以 out 模式打開的文件內容,我們必須同時指定 app 模式,這樣才會以追加的模式寫到文件末尾:或者同時指定 in 模式,即打開文件同時進行讀寫操作。
6.ate 和 binaty 模式可用於任何類型的文件流對象,且可以與其他任何文件模式組合使用。
每個文件流類型都定義了一個默認的文件模式,當我們未指定文件模式時,就使用此默認模式。ifsream 關聯的文件默認以 in 模式打開,ofstream 關聯的文件默認以 out 模式打開,fstream 關聯的文件默認以 in 和 out 模式打開。
對流的操作:
對輸入流操作:seekg()與tellg()
對輸出流操作:seekp()與tellp()
下面以輸入流函數為例介紹用法:
seekg() 是對輸入文件定位,它有兩個參數:第一個參數是偏移量,第二個參數是基地址。
對於第一個參數,可以是正負數值,正的表示向後偏移,負的表示向前偏移。而第二個參數可以是:
ios::beg:表示輸入流的開始位置
ios::cur:表示輸入流的當前位置
ios::end:表示輸入流的結束位置
tellg() 函數不需要帶參數,它返回當前定位指針的位置,也代表著輸入流的大小。
1 #include <iostream> 2 #include <fstream> 3 using namespace std; 4 5 const char *filename = "D:\\code\\c++\\ac27.text";//該文件中存儲了abcd四個字符 6 7 int main(void){ 8 long l, m; 9 ifstream in(filename, ios::in | ios::binary | ios::ate);//加了ate參數,此時get指針在文件末尾(‘\0‘)的下一個位置 10 l = in.tellg(); 11 in.seekg(0, ios::end);//將get指針移動到文件末尾下一個位置 12 m = in.tellg();//得到get指針當前的位置 13 in.close(); 14 cout << l << endl;//輸出6 15 cout << "size of " << filename; 16 cout << " is " << (m - 1) << " byset.\n";//m-1為5 17 return 0; 18 }
對於輸出流操作:seekp()與tellp()用法是一樣的。
IO相關2(文件輸入輸出)