第八 九章 I/O 系統 檔案 I/O 的基本概念 格式化 I/O 建立自訂的嵌入子 建立自訂的擷取子 自訂 I/O 與檔案
檔案 I/O 的基本概念 C++ 對 I/O 的支援是放在 <iostream> 中, 而 Class ios 包括有許多成員函數與變數可以用來控制和監督串流的運作. 要處理檔案 I/O, 心必須 #include<fstream>, 它包含了 ifstream,ofstream and fstream, 而這些 class 是衍生自 istream,ostream 而此兩個 class 是衍生自 ios
檔案 I/O 的基本概念 在 C++ 中, 開啟一個 File 就是鏈結到一個 stream; 而 stream 有三種型態 : ifstream in; // input ofstream out; // output fstream io; // input and output 一旦建立 stream 後, 利用 open() 便可以將 stream 與 file 鏈結在一起.
OPEN() 函數 void ifstream::open(const char *filename,openmode mode=ios::in); void ofstream::open(const char *filename,openmode mode=ios::in ios::trunc); void fstream::open(const char *filename,openmode mode=ios::in ios::out);
Openmode ios::app // 所有的輸出附加到檔案結尾 ios::ate // 將檔案指標移到檔案結尾 ios::binary // 以二進位模式開啟檔案 ios::in // 開啟檔案作為輸入使用 ios::out // 開啟檔案作為輸出使用 ios::trunc // 會覆寫之前同名的檔案
使用 Open 函數開啟檔案 Ofstream mystream; Mystream.open( test ); 判斷開啟與否 If(!mystream){cout<< cannot open file\n ;} 或可利用 is_open() 判斷開啟與否 Bool is_open(); 如果開啟 file 則傳回 true; 否則則傳回 false; If(!mystream.is_open()){{cout<< cannot open file\n ;}
Stream 的建構函數 儘管可以使用 open() 開檔, 但更方便的方法則是使用 ifstream mystream( myfile );//open file 因為在 ifstream,ofstream,fstream 中的建構函數會自動開啟檔案. 判斷是否到檔案結尾則是用 bool eof(); 關閉檔案則是用 close();
檔案內容的讀寫 當 file 己開啟時, 不在使用 cin 與 cout 來讀寫檔案, 而是改成 >> 與 <<, 就像是 c 中的 fprintf() 與 fscan().
開關檔範例 1 #include<iostream> #include<fstream> using namespace std; int main() { ofstream fout("c:\\test.dat"); if(!fout){ cout<<"cannot open file\n";return 1; } fout<<"hello\n"; fout<<100<<' '<<hex<<100<<endl; fout.close(); ifstream fin("c:\\test.dat"); if(!fin){ cout<<"cannot open file\n";return 1; } char str[80]; int i; fin>>str>>i; cout<<str<<' '<<i<<endl; fin.close(); return 0;}
課後練習 寫一程式來計算檔案中的字元個數.
格式化的 I/O 在每個 stream 中都有一格式化 flag 的集合, 可以用來控制資料的格式 ; 在 class ios 中就宣告了一列舉型別 fmtflags 它定義了 skipws // 忽略空白字元 left // 輸出向左靠攏 oct // 八進位輸出表示 hex // 十六進位輸出表示 uppercase // 字元輸出變大寫
格式化的 I/O showpos // 顯示正數前加 + 符號 showpoint // 在 float 時顯示小數點和其後的零 scientific // 以科學符號顯示 fixed // 以一般方式顯示要設定一格式旗標使用 ios 的成員函數 setf() // fmtflags setf(fmtflags flags) ex: stream.setf(ios::showpos);
格式化的 I/O 而 unseft() 函數可用來清除 flag 設定值 void unseft(fmtflags flags); 要了解目前 flag 的設定則使用 flag() 函數 fmtflags flag(); fmtflags flag(fmtflags f);
設定多種旗標格式範例 #include<iostream> using namespace std; int main(){ // show using default setting cout<<123.23<<"hello <<100<<'\n'; cout<<10<<' '<<- 10<<'\n'; cout<<100.0<<"\n\n"; //now, change formats cout.unsetf(ios::dec); cout.setf(ios::hex ios::scie ntific); cout<<123.23 <<"hello" <<100<<'\n'; cout.setf(ios::showpos); cout<<10<<' '<<- 10<<'\n'; cout.setf(ios::showpoint io s::fixed); cout<<100.0<<"\n\n"; return 0; }
改變大小寫範例 #include<iostream> using namespace std; int main(){ cout.unsetf(ios::dec); cout.setf(ios::uppercase ios::showbase ios::hex ); cout<<88<<"\n\n"; cout.unsetf(ios::uppercase); cout<<88<<"\n\n"; return 0; }
練習 寫一程式來設定 cout flag. 在 show float 時可以 show 出小數點 ; 另外以科學技號再加上大寫 E 來 show 出所有的 float.
建立自訂的嵌入子 在 C++ 中, 輸出的動作被叫作嵌入, 而 << 則被稱為嵌入運算子 (insertion operator) ostream& & operator<<(ostream ostream& out, class-name ob) { // body or inserter return stream;}
建立自訂的嵌入子 第一個參數是對 ostream 的參考型態, 這代表 stream 必須要是一個 output stream 第二個參數則代表要輸出的物件 ( 也可以是一參考參數 ) 嵌入函數傳回 stream 的參考, 這是為了讓 << 能在一連串的 I/O 運算中使用 Ex:cout<<ob1<<ob2<<ob3;
建立自訂的嵌入子 由於嵌入子左運算元是一 stream; 而右運算元是要輸出的物件, 如果將嵌入子函數放進 class 變成成員函數, 它的左運算元會自動由 this 指標被設為產生這個呼叫的物件. 因此嵌入子不能放入 class 中. 為了讓嵌入子函數可存取 class 中的成員則嵌入子要變為該 class 中的 friend.
嵌入子範例 - 成為 friend #include<iostream> using namespace std; class coord{ int x,y; public: coord(){x=0;y=0;} coord(int a,int b){x=a;y=b;} friend ostream &operator<<(ostream &stream,const coord& ob); };
嵌入子範例 - 成為 friend ostream &operator<<(ostream &stream, const coord& ob) {stream<<ob.x<<", "<<ob.y<<'\n'; return stream;} int main() { coord a(1,1),b(2,3); cout<< a<< b; return 0;}
嵌入子範例 - 不成為 friend #include<iostream> using namespace std; class coord{ public: int x,y; coord(){x=0;y=0;} coord(int a,int b){x=a;y=b;} // friend ostream &operator<<(ostream &stream, const coord ob); };
嵌入子範例 - 不成為 friend ostream &operator<<(ostream &stream,const coord ob) { stream<<ob.x<<", "<<ob.y<<'\n'; return stream;} int main() { coord a(1,1),b(2,3); cout<<a<<b; return 0; }
建立自訂的擷取子 在 C++ 中, 輸入的動作被叫作擷取運算子 (extraction operator), istream& & operator>>(istream istream& stream, class-name ob) { // body or inserter return stream;}
擷取子範例 #include<iostream.h> //using namespace std; class coord{ int x,y; public: coord(){x=0;y=0;} coord(int a,int b){x=a;y=b;} friend istream &operator>>(istream &stream,coord& ob); friend ostream &operator<<(ostream &stream,coord& ob); };
擷取子範例 istream &operator>>(istream &stream, coord& ob) { cout<<"enter coordinates\n"; stream>>ob.x>>ob.y; return stream; } ostream &operator<<(ostream &stream, coord& ob) { stream<<ob.x<<", "<<ob.y<<'\n'; return stream; }
擷取子範例 int main() { coord a(1,1),b(2,3); cout<<a<<b; cin>>a; cout<<a; return 0; }
自訂 I/O 與檔案 結合開檔與 >> 和 << 兩個運算子 #include<iostream> #include<fstream> using namespace std; class coord{ int x,y; public: coord(){x=0;y=0;} coord(int a,int b){x=a;y=b;} friend ostream &operator<<(ostream &stream, coord &ob); friend istream &operator>>(istream &stream,coord &ob); };
自訂 I/O 與檔案 ostream &operator<<(ostream &stream,coord& ob) { stream<<ob.x <<'\n'; stream<<ob.y<<'\n'; return stream;} istream &operator>>(istream &stream,coord &ob) { stream>>ob.x>>ob.y; return stream;} int main() {
自訂 I/O 與檔案 coord a(1,1),b(2,3); ofstream out("c:\\test.dat"); if(!out){cout<<"cannot open output file\n"; return 1; } out<<a<<b; out.close(); ifstream in("c:\\test.dat"); if(!in){cout<<"cannot open input file\n"; return 1;}
自訂 I/O 與檔案 coord c(0,0),d(0,0); in>>c>>d; cout<<c<<d; in.close(); return 0; }
練習 請撰寫一 class, 它可以儲存一整數值和它最小的因數 (factor), 請加入嵌入子與擷取子.