C++学习笔记:(九)输入/输出流
目錄
9.輸入/輸出流
9.1C++流類庫簡介
9.2輸入/輸出流
9.3自定義類型的輸入/輸出
9.4文件輸入/輸出
9.輸入/輸出流
數據的輸入、輸出是最重要的操作,C++ 的輸入、輸出有 iostream 庫提供支持。它利用多繼承和虛擬繼承實現了面向對象類層次結構。C++ 的輸入、輸出機制為內置數據類型的輸入、輸出提供了支持,同時也支持文件的輸入、輸出。 在此基礎上,設計者可以通過擴展 iostream 庫,為新類型的數據讀寫進行擴展。
?
9.1 C++流類庫簡介
C 語言中的 scanf 和 printf 很靈巧高效,但不是類型安全的,而且沒有擴展性。scanf/printf 系列函數要求把讀寫的變量和控制讀寫的格式信息分開。而 C++ 正是克服了這些缺點,使用 cin/cout 控制輸入/輸出。(類型安全就是指兩個類型要相互轉換,則必須要顯示的轉換,而不能隱式的只用一個賦值操作就完成轉換)
(1) cin,表示標準輸入的 istream 類對象。cin 從終端讀入數據。
(2) cout,表示標準輸出的 ostream 類對象。cout 向終端寫數據。
(3)?cerr,表示標準錯誤輸出(非緩沖方式),導出程序錯誤信息。
(4)?clog,表示標準錯誤輸出(緩沖方式),導出程序錯誤信息。
為了在程序中使用 cin/cout,必須要加 iostream 庫的相關頭文件 #include <iostream>
iostream 類同時從 istream 類和 ostream 類派生而來,允許雙向輸入/輸出。輸入有重載的操作符 >> 完成,輸出由重載的操作符 << 來完成。輸入輸出格式:
cin >> 變量名; cout << 變量名或常量;除了用戶終端的輸入/輸出,C++ 還提供了文件的輸入/輸出類:
(1) ifstream,從 istream 派生而來,把文件綁定到程序上輸入。
(2) ofstream,從 ostream 派生而來,把文件綁定到程序輸出。
(3) fstream,從 iostream 派生而來,把文件綁定到程序輸入/輸出。
使用 iostream 庫的文件流,必須包含相關的頭文件:#include <fstream>
文件讀寫示例:
#include <iostream> #include <string> #include <fstream> using namespace std;int main() {string srsfname("asdfile.txt"); //定義源文件ifstream srsfile(srsfname.c_str()); //打開源文件流類if(!srsfile) //失敗給出信息{cout << "Error:unable to open the infile:" << srsfname <<endl;return -1;}string purfname("purfile.txt"); //定義目標文件ofstream purfile(purfname.c_str()); //打開目標文件流類if(!purfile) //失敗給出信息{cerr << "Error:unable to open the infile:" << purfname <<endl;return -1;}string buf;while(srsfile >> buf){purfile << buf; //輸入到目標文件}return 0; }?
9.2 輸入/輸出流
9.2.1 基本輸出流
C++ 的 ostream 提供了豐富的格式化和無格式化的輸出功能:用流插入符輸出標準數據類型;put 庫成員函數輸出字符;以八進制、十進制、十六進制輸出數據;以各種精確方式輸出浮點型數據;以科學計數法定點輸出數據;以指定寬度輸出數據;在區域內用指定字符填充等。
流輸出可以用流插入運算符 —— 即重載的 << 來完成。運算符 << 左邊的操作數是 istream 類的一個對象(如cout),右邊可以是 C++ 的合法表達式。
用插入運算符實現流輸出:
using namespace std; int main() {cout << "hello! C++\n";cout << "hello! C++"?<<endl;return 0; }輸出操作符可以接受任何內置數據類型的實參,包括 const char*,以及標準庫 string 和 complex 類類型。任何表達式包括函數調用,都可以是輸出操作符的實參,只要它的計算結果是一個被輸出操作符實例接受的數據類型即可。
函數調用作操作符的實參:
using namespace std; int main() {cout << "hello! C++";cout << strlen("hello! C++") <<endl;return 0; }C++ 還提供了指針預定義輸出操作符,允許輸出項為顯式對象的地址。默認情況下,地址以十六進制的形式顯式。
#include <iostream> using namespace std;int main() {int i = 10;int *p = &i;cout << "i:" << i << " &i:" << &i <<endl;cout << "*p" << *p << " p:" << p << " &p:" << &p <<endl;return 0; } #include <iostream> using namespace std;int main() {int i = 10, j = 20;cout << "The max is ";cout << (i>j)?i:j;cout <<endl;return 0; }說明:問題在于輸入操作符的優先級高于條件操作符,所以輸出 i、j 的比較結果的 bool 值。即表達式被解析為:( cout << ( i > j ) ) ? i : j; 。因為 i < j,所以結果為 false,輸出結果 0。為了得到正確結果,整個表達式必須放在括號中:cout << ( ( i > j ) ? i : j );。由此得到正確結果:the max is 20。
?
9.2.2 基本輸入流
對應于輸出,C++ 提供了實用的輸入功能。類似于輸出流中的流插入符,輸入中引入了流讀取符,也稱提取符。
流輸入可以用流讀取運算符 —— 即重載的 >> 來完成。類似于輸出運算符 <<,讀取符是個雙目運算符,左邊的操作數是 istream 類的一個對象(如 cin),右邊的操作數是系統定義的任何數據類型的變量,例如:int i; cin >> i;從鍵盤輸入的數據會自動轉換成 i 的類型,存儲到變量 i 中。
注意:(1) 輸入運算符 >> 也支持級聯輸入。在默認情況下,運算符 >> 跳過空格,讀入后面與變量類型相應的值。因此給一組變量輸入值時,用空格或換行將輸入的數值間隔開,例如:
int i; float f; cin >> i >> f;(2) 當輸入字符串(char* 類型)時,輸入運算符 >> 會跳過空格,讀入后面的非空格符,直到遇到另外一個空格結束,并在字符串末尾自動放置字符 '\0' 作為結束標志,例如:
char s[20]; cin>> s;(3) 數據輸入時,不僅檢查數據間的空格,還做類型檢查、自動匹配,例如:
int i; float f; cin >> i >> f;?
9.2.3 格式化輸入/輸出
在很多情況下,用戶希望自己控制輸出格式。C++ 提供了兩種格式控制方法:通過使用 ios 類中的格式控制函數;使用稱為操縱符的特殊類型函數。
ios 類成員函數控制格式
在 ios 類中,格式控制函數主要是通過對狀態字、域寬、填充字及其精度來完成的。輸入/輸出格式由一個 long int 類型的狀態字確定。在 ios 類的 public 部分進行了枚舉。
skipws ??????????? 跳過輸入中的空白 left ??????????????左對齊格式輸出 right ???????????? 右對齊格式輸出 internal ??????????在符號位和數值之間填入字符 dec ???????????? ?十進制顯示 oct ???????????? ?八進制顯示 hex ???????????? ?十六進制顯示 showbase ??????? ?產生前綴,指示數值的進制基數 shwopoint ??????? ?總是顯示小數 uppercase ??????? ?在十六進制下顯示0X,科學計數法顯示E showpos ??????? ?在非負數值中顯示+ boolalpha ??????? ?把true和false表示為字符串 scientific ????????以科學計數法形式顯示浮點數 fixed ??????????? ?以小數形式顯示浮點數 unitbuf ???????? ??輸出操作后立即刷新所有流 stdio ?????????? ??輸出操作后刷新stdout和stderr(1) 設置狀態標志
設置狀態標志可以使用 long ios::setf(long flags) 函數,使用格式如下:
stream_obj.setf(ios::enum_type);說明:其中,stream_obj 為被影響的流對象,常用的是 cin 和 cout。enum_type 為上述列舉的值。例如:cout.setf(ios::dec); 表示以十進制輸出。要設置多個標志時,彼此間用運算符 " | " 連接(不能連接相反的格式控制符,如:不能 skipwsl|noskipws)。
例如:cout.setf(ios::dec|ios::scientific);//表示以十進制、科學計數法輸出。(2) 清除狀態標識
清除狀態標識可使用 long ios::unsetf(long flags) 函數。使用格式如下:
stream_obj.unsetf(ios::enum_type);使用方法與 ios::setf() 函數相同。
(3) 取狀態標識
取狀態標志用 flags() 函數,在 ios 類中重載了兩個版本:
long ios::flags(); long ios::flags(long flag);前者用于返回當前狀態標志字;后者將狀態標志字存儲在 flag 內。注意的是與 setf() 函數設置狀態標志字不同,flags() 函數是取狀態標志字的。
應用實例:
#include <iostream> using namespace std;void showflags(long f) //輸出標志字函數 {long i;for(i = 0x8000; i; i = i>>1) //使用右移位{if(i&f) //某位不為0,則輸出1,否則為0cout << "1";else cout << "0";}cout <<endl; }int main() {long flag;flag = cout.flags();showflags(flag);cout.setf(ios::dec|ios::boolalpha|ios::skipws);flag = cout.flags();showflags(flag);cout.unsetf(ios::skipws);flag = cout.flags();showflags(flag);return 0; }函數 showflags() 的功能是輸出狀態字的二進制數值。算法思想是,從最高位到最低位,逐位計算與二進制 1 的位與(即從 0x8000 開始逐個移位循環 16 次到 0x0001),并輸出該二進制位的數值(只有二進制位為 1 時,結果才為 1,否則為 0),由此得到狀態字各個二進制位的數值。
第一行顯式的是系統默認狀態下的狀態標志字值,skipws 和 unitbuf 被設定(查相應的標志數值表得到)。第二行顯式的默認狀態下,并追加了 ios::dec|ios::boolalpha|ios::skipws 的狀態標志, skipws 是已有標志,沒有變化相應的二進制位,而 ios::dec 和 ios::boolalpha 位發生了變化。第三行顯式的是去掉 ios::skipws(執行函數 unsetf() 后得到)后狀態字的變化,顯式最后一位是控制 ios::skipws 的標志位。
(4) 設置域寬
域寬用于控制輸出格式,在 ios 類中重載了兩個函數來控制域寬,原型如下:
int ios::width(); int ios::width(int w);第一個函數得到當前的域寬值;第二個函數用來設置新域寬,并返回原來的域寬值。需要注意的是:所設置的域寬僅僅對下一個輸出的操作有效,當完成一次輸出操作后,域寬值又恢復為 0。
(5) 設置填充字符
填充字符的作用是,當輸出值不滿足域寬時用設定的字符來填充,默認情況下填充的字符為空格。實際應用中填充字符函數與設置域寬函數 width() 配合使用,否則無空可填,毫無意義。ios 類提供了兩個重載的成員函數來操作填充字符,原型如下:
char ios::fill(); char ios::fill(char c);第一個函數返回當前使用的填充字符;第二個函數設置新的填充字符,并返回設置前的填充字符。
(6) 設置顯示精度
類似地,ios 類也提供了重載的兩個函數來控制顯示精度,函數原型如下:
int ios::precision(); int ios::precision(int num);第一個函數返回當前設置精度值;第二個函數設置新的顯示精度。
輸入/輸出實例:
#include <iostream> using namespace std;int main() {int i = 123;float f =2018.0903;const char* const str = "hello! every one!";cout << "default width is:" << cout.width() <<endl; //默認域寬cout << "default fill is:" << cout.fill() <<endl; //默認的填充字符cout << "default precision is:" << cout.precision() <<endl; //默認精度cout << "i = " << i << " f = " << f << " str = " << str <<endl;cout << "i = ";cout.width(12); //設域寬cout.fill('*'); //設填充字符cout << i <<endl;cout << "i(hex) = ";cout.setf(ios::hex, ios::basefield); //設十六進制cout << i;cout << " i(oct) = ";cout.setf(ios::oct, ios::basefield); //八進制cout << i <<endl;cout << "f = ";cout.width(12);cout.precision(3); //設精度cout << f <<endl;cout <<"f = ";cout.width(12);cout.setf(ios::fixed, ios::floatfield);cout << f <<endl;cout << "str = ";cout.width(20);cout.setf(ios::left); //設左對齊cout << str <<endl;return 0; }?
操縱符控制格式
通過上面代碼發現,使用 ios 類成員函數控制輸入、輸出格式,必須靠流對象來調用,而且不能直接嵌入到輸入、輸出語句中,使用不夠簡潔方便。C++ 提供了另一中控制格式的方法,稱為操縱符方法,類似于函數的運算符。使用操縱符方法可以嵌入到輸入、輸出語句中,使用簡潔方便。
所有不帶參數的操作符定義在頭文件 iostream.h 中,帶形參的操縱符定義在頭文件 iomanip.h 中,使用操縱符時需要包含相應的頭文件。常用的有:
ws ??????????????????? 在輸入時跳過開頭的空白符,僅用于輸入 endl ?????????????????? 換行并刷新輸出流,僅用于輸出 ends ????????????????? ?插入一個空字符,僅用于輸出 dec ??????????????????? 十進制顯示 oct ??????????????????? 八進制顯示 hex ??????????????????? 十六進制顯示 setfill(ch) ????????????用ch填充空白字符 setprecision(n) ????????將浮點數精度設置為n setw(w) ????????????????按照w個字符來讀或寫數值 setbase(b) ?????????????以進制基數b來輸出整數值 setiosflags(n) ?????????設置由n指定的格式標志 resetiosflags(n) ???????清除由n指定的格式標志在進行輸入、輸出時,操縱符被嵌入到輸入、輸出語句中,用來控制格式。使用操縱符與成員函數的對比:
#include <iostream> #include <iomanip> using namespace std;int main() {int i = 123;float f =2018.0903;const char* const str = "hello! every one!";cout << "default width is:" << cout.width() <<endl;cout << "default fill is:" << cout.fill() <<endl;cout << "default precision is:" << cout.precision() <<endl;cout << "i = " << i << " f = " << f << " str = " << str <<endl;cout << "i = " << setw(12) << setfill('*') << i <<endl;cout << "i(hex) = " << hex << i << " i(oct) = " << oct << i << endl;cout << "f = " << setw(12) << setprecision(3) << f <<endl;cout << "f = " << setw(12) << fixed << f <<endl;cout << "str = " << setw(20) << left << str <<endl;return 0; }?
用戶自定義的操縱符控制格式
在 C++ 中,除了系統提供的預定義操縱符之外,還允許用戶自己定義操縱符,便于控制一些頻繁使用的格式操作,使得格式控制更加方便。
自定義輸出流操縱符算子格式如下:
ostream & 自定義輸出操縱符算子函數名 (ostream& stream) {...//自定義代碼return stream; }自定義輸入流操縱符算子格式如下:
istream & 自定義輸出操縱符算子函數名 (istream& stream) {...//自定義代碼return stream; }自定義輸出操作函數:
#include <iostream> #include <iomanip> using namespace std;ostream &myoutput(ostream& stream) {stream << setw(10) << setfill('#') <<left;return stream; }int main() {int i = 123;cout << i <<endl;cout << myoutput << i <<endl;return 0; }自定義操縱符函數 myoutput(),功能是設置域寬為 10,填充字符為 #,并采用左對齊。函數參數為引用方式,cout 的返回值為引用方式且可以多級輸出,所以在主函數中只寫 myoutput 即可。
自定義輸入操作函數:
#include <iostream> using namespace std;istream &myinput(istream& stream) {cout << "please input a hex integer:";stream >> hex;return stream; }int main() {int i;cin >> myinput >> i;cout << "i(dec) = " << i << " i(hex) = " << hex << i <<endl;return 0; }?
9.2.4 其他的輸入/輸出函數
正如在 C 語言中遇到的問題一樣,輸入字符串時,僅靠 cin 只能得到不包括空格的字符串。針對此類問題,C++ 還提供了以下輸入/輸出的函數。
get()、put()函數get(char& ch) 從輸入流中提取一個字符,包括空白字符,并把它存儲在 ch 中,返回被應用的 istream 對象。此函數在類 istream 里。對應于 get(),類 ostream 提供了 put(char ch) 函數,用于輸出字符。
get()的重載版本:get(char *str, streamsize size, char delimiter = '\n');str 代表一個字符數組,用來存放被讀取的字符。size 代表可以從 istream 中讀入的字符的最大數目。delimiter 表示如果遇到它就結束讀取字符的動作,delimiter 本身不會被讀入,而是留在 istream 中,作為 istream 的下一個字符。一個常見的錯誤是,執行第二個 get() 時省略 delimiter。下面例子使用 istream 的成員函數 ignore() 來去掉 delimiter。默認情況下,delimiter 為換行符。
get/put 函數實例(直接換行結束):
#include <iostream> using namespace std; int main() {const int str_len = 100;char str[str_len];while(cin.get(str,str_len))//當讀入的數據不為空時循環下一次,每次最多讀入str_len個{int count = cin.gcount(); //當前實際讀入多少個字符cout << "the number is:" << count <<endl;if(count < str_len)cin.ignore(); //在下一行之前去掉換行符}return 0; }getline() 函數
使用 get() 函數輸入字符串時,經常忘記去掉 delimiter,所以引入函數 getline(),因為 getline() 會丟棄終結符。其原型與 get() 的重載版本相同:
getline(char *str, streamsize size, char delimiter = '\n');write()、read() 函數
ostream 類成員函數 write() 提供一種輸出字符數組的方法。它不是輸出 "直到終止字符為止",而是輸出某個長度的字符序列,包括空字符。函數原型如下:
write(char *str, streamsize length);str 是要輸出的字符數組,length 是要顯示的字符個數。write() 返回當前被調用的 ostream 類對象。
istream 類的 read() 函數,原型如下:
read(char *str, streamsize size);read() 函數從輸入流中讀取 size 個連續的字符,并將其放在地址從 str 開始的內存中。gcount() 方法返回最后一個非格式化的抽取方法讀取的字符數。這意味著字符由 get()、getline()、ignore()、或 read() 方法讀取的,不是由抽取操作符( >> )讀取的,抽取操作符對輸入進行格式化,使之與特定的數據類型匹配。
輸入/輸出綜合實例:
#include <iostream> using namespace std;int main() {const int str_len = 100;char str[str_len];int iline = 0; //行數int max = -1; //最長行的長度while(iline < 3){cin.getline(str, str_len);int num = cin.gcount();cout << "the number is:" << num <<endl;iline++;if(num > max)max = num;cout << "Line#" << iline << "\t chars read:" << num <<endl;cout.write(str, num).put('\n').put('\n');}cout << "Total lines:" << iline <<endl;cout << "The longest line:" << max <<endl;return 0; }?
9.3自定義類型的輸入/輸出
當實現一個類的類型時,有時希望這個類支持輸入和輸出操作,以便可以將類對象嵌入到輸入或輸出流中。前面的函數都是 C++ 系統預定義數據類型的輸入/輸出。對于用戶自己定義的數據類型的輸入/輸出,可以通過重載運算符 ">>" 和 "<<" 實現。
?
9.3.1 重載輸出運算符 "<<"
其重載函數的格式如下:
ostream & operator << (ostream &out, class_name &obj) {out << obj.data1;out << obj.data2;......out << obj.datan;return out; }函數中第一個參數 out 時對 ostream 對象的引用,即 out 必須是輸出流對象;第二個參數是用戶自定義要輸出的類對象的引用。data1,data2,... datan 是類內要輸出的數據成員。輸出運算符 "<<" 不能作為類的成員函數,只能作為友元函數(要訪問類的私有成員)來實現。
輸出運算符重載:
#include <iostream> #include <string.h> using namespace std;class Word {char *word;size_t num; //存儲的字符個數public:Word(const char* const str = NULL);virtual ~Word(){if(word) //清除指針delete []word;}friend ostream& operator<<(ostream& out, Word& sword); };Word::Word(const char* const str) {if(str != NULL){num = strlen(str);word = new char[num+1];strcpy(word,str);} }ostream& operator<<(ostream& out, Word& sword) {out << "<" << sword.num << ">" << sword.word <<endl;return out; }int main() {Word word("hello");cout << word;return 0; }程序為類 word 重載輸出運算符 "<<",使得可以通過 cout 簡單地輸出 word 類對象的內容:字符串和字符串長度。在類 word 的構造函數里,為類成員 word 申請空間(包括字符串結束標志 '\0'),在析構函數中,相應地釋放成員 word 的空間。重載函數的第二個參數為引用形式,可以轉為普通形式,但是引用減少了調用開銷。
?
9.3.2 重載輸入運算符?">>"
輸入運算符 ">>",又稱為提取運算符,定義其重載函數的格式如下:
istream &operator>>(istream &in, class_name& obj) {in >> obj.data1;in >> obj.data2;......in >> obj.datan;return out; }函數中第一個參數 in 是對 istream 對象的引用,即 in 必須是輸入流對象;第二個參數是用戶自定義要輸入的類對象的引用。data1,data2 ... datan 是類內要輸入的數據成員。輸入運算符也不能作為類的成員函數,只能作為類的友元函數或獨立函數。重載函數第二個參數必須為引用,且不能為常引用,因為要對參數進行修改。
輸入運算符重載:
#include <iostream> #include <string.h> using namespace std;class Word {char *word;size_t num;public:Word(const char* const str = NULL);virtual ~Word(){if(word)delete []word;}friend ostream& operator<<(ostream& out, Word& sword);friend istream& operator>>(istream& in, Word& sword); };Word::Word(const char* const str) {if(str != NULL){num = strlen(str);word = new char[num+1];strcpy(word,str);} }ostream& operator<<(ostream& out, Word& sword) {out << "<" << sword.num << ">" << sword.word <<endl;return out; }istream& operator>>(istream& in, Word& sword) {char str[100];in.getline(str, 100);if(in.gcount() > 0){delete []sword.word;sword.num = strlen(str);sword.word = new char[sword.num+1];strcpy(sword.word, str);}return in; }int main() {Word word("hello");cout << word;cin >> word;cout << word;return 0; }?
9.4 文件輸入/輸出
變量中的數據保存在內存中,是臨時的;文件數據保存在外存儲器上,用于永久的保存大量的數據。根據數據的組織形式,文件分為文本文件和二進制文件。文本文件的每個字節存放一個 ASCII 代碼,代表一個字符;二進制文件把內存中的數據,按照其在內存中的存儲形式原樣寫到磁盤上存放。
C++ 把每個文件看成一個有序的字節流(字符流或二進制流)。每個文件不是以文件結束符結束,就是以在系統維護和管理的數據結構中特定的字符結束。文件打開時,就會創建一個對象,將這個對象和某個流關聯起來。C++ 中,要進行文件的輸入/輸出,必須首先創建一個流,與文件關聯,才能進行讀寫操作。
C++ 進行文件處理時,需要包含頭文件 <iostream.h> 和 <fstream.h>。<fstream.h> 頭文件包括流類 ifstream(從文件輸入)、ofstream(向文件輸出)和 fstream(從文件輸入/輸出)的定義。這三個流分別從 istream、ostream 和 iostream 類繼承而來。#include<iostream.h> 是非標準輸入輸出流,#include<iostream> 是標準輸入輸出流。C++ 中為了避免名字定義沖突,特別引入了 "名字空間的定義",即 namespace。當代碼中用 <iostream.h> 時,輸出可直接引用 cout<<x;,<iostream.h> 繼承 C 語言的標準庫文件,未引入名字空間定義,所以可直接使用。當代碼中引入 <iostream> 時,輸出需要引用 std::cout<<x; 如果還是按原來的方法就會有錯。使用 <iostream> 時,引入 std:: 有以下方法:
① using namespace std;?cout << x;② using std::cout;cout << x;③ 最基本的std::cout << x;其他頭文件也是同樣的道理。(有 ".h" 的就是非標準的,C 的標準庫函數。無 ".h"的,就要用到命令空間,是 C++ 的。還有一部分不完全是有 ".h" 和沒 ".h" 的差別。例如:math.h和cmath)。
?
9.4.1 順序訪問文件
C++ 把文件視作無結構的字節流,所以記錄等說法在 C++ 中不存在。要正確地進行文件的輸入\輸出,需要遵循以下步驟。
(1) 定義相關的流對象
包含頭文件 <fstream.h> 建立流,定義流對象。
例如:
ifstream in; ????//輸入文件流 ofstream out; ???//輸出文件流 fstream inout; ??//輸入/輸出文件流(2) 文件的打開
打開文件有兩種方式。
①:使用 open() 函數
open 函數是 ifstream、ofstream 和 fstream 類的成員函數。原型定義如下:
void open ( const char *,ios_base::openmode mode = ios_base::in | ios_base::out ); void open ( const unsigned char *, int mode, int access = filebuf::openprot );第一個參數用來傳遞文件名稱;第二個參數 mode 決定文件的打開方式第三個。
打開方式: ios::app ??????????? 將輸出寫入文件末尾 ios::ate ??????????? 打開文件,并將文件指針移到文件末尾 ios::in ???????????? 打開文件進行輸入 ios::out ?????????? ?打開文件進行輸出 ios::nocreate ???????文件不存在,則open()失敗 ios::noreplace ??????文件存在,則open()失敗 ios::trunc ??????????刪除文件中現有數據(覆蓋同名文件) ios::binary ?????????以二進制方式打開,進行輸入/輸出(默認方式為文本方式)打開文件的屬性: 0 普通文件,打開操作 1 只讀文件 2 隱含文件 4 系統文件 例如: ofstream out; out.open("test.tt", ios::out); //表示用輸出流對象out打開一個"test.tt"文件。當一個文件需要多種方式打開時,可以用”或”操作符把幾種方式連接在一起。例如打開一個能用于輸入\輸出的二進制文件:
fstream inout; inout.open("test.tt", ios::in | ios::out | ios::binary);打開文件后要判斷是否打開成功:
if(inout.is_open()) ???//如果打開成功則進行讀寫操作 {...}?
②:流類構造函數打開
雖然完全可以用 open() 函數打開文件,但是類 ifstream、ofstream 和 fstream 中的構造函數都有自動打開文件功能,這些構造函數的參數及默認值與 open() 函數完全相同。因此,打開文件的最常見形式簡化為:文件輸入/輸出流類 ?流對象名("文件名稱");
例如:ofstream out("test.tt");使用構造函數打開文件后,直接使用流對象判斷是否打開成功。
if(!out) {cout << "文件打開失敗!"?<<endl;//錯誤處理 }(3) 文件的讀寫
文件一旦打開,從文件中讀取數據或向文件中寫入數據將變得十分容易,只需要使用運算符 ">>" 和 "<<" 就可以了,只是必須用與文件相連接的流對象代替 cin 和 cout。例如:
ofstream out("test.tt"); if(out) {out << 10 << "hello!\n";out.close(); }(4) 文件的關閉
使用完文件后,應該把它關閉,即把打開的文件與流分開。對應于 open() 函數,使用 close() 函數關閉文件。在流對象的析構函數中,也具有自動關閉功能。
① 文本文件讀寫(文件的打開方式默認為文本方式)
#include <iostream> #include <fstream> using namespace std;int main() {char str[20] = "Welcome to C++!";int i = 1234;char ch;ofstream out("test.tt"); //打開輸出文件流if(out) //如果成功{out << str << '\n' << i; //向文件輸入信息out.close(); //關閉}ifstream in("test.tt"); //打開輸入文件流if(in) { while(in.get(ch)) //輸出顯示cout << ch;cout <<endl;in.close();}return 0; }② 二進制文件讀寫
文本文件中存放的是字符,而二進制文件中存放的是二進制位。對二進制文件讀寫有兩種方式,一種是使用 get() 和 put() 函數。一種是使用 read() 和 write() 函數。當然,這 4 個函數也可用于文本文件的讀寫。
get() 函數和 put() 函數原型如下:
istream& get(unsigned char& ch); ostream& put(char ch);get() 是類 istream 的成員函數,功能是從文件流中只讀取一個字節,并把值放在 ch 中,put() 是類 ostream 的成員函數,功能是向文件流中寫入一個字節 ch。當讀文件過程中遇到結束符時,與之相連的文件流值變為 0,所以可以根據此判斷文件是否結束。
二進制文件讀寫實例:
#include <iostream> #include <fstream> using namespace std;void myread(const char* fname) {ifstream file_read(fname, ios::binary);char c;if(file_read){while(file_read.get(c)) //文件沒有結束時cout << c; } }void mywrite(const char* fname) {ofstream file_write(fname, ios::binary);char c = 'A';if(file_write){for(int i = 0; i < 26; i++)file_write.put(c+i);} }int main() {char fname[20] = "word.file";mywrite(fname);myread(fname);cout <<endl;return 0; }read() 函數和 write() 函數原型如下:
istream& read(unsigned char* buf, int num); ostream& write(const unsigned char* buf, int num);有時需要讀寫一組數據到文件,read() 函數是類 istream 的成員函數,它從相應的流中讀取 num 個字節放在 buf 所指向的緩沖區中。write() 函數是 ostream 的成員函數,它從 buf 所指的緩沖區中向相應的流寫入 num 個字節。例如:
int list[] = {12, 13, 14, 15}; write((unsigned char*)&list, sizeof(list));定義了一個整形數組 list,為了寫入它的全部數據,必須在函數 write() 中指定它的首地址 &list,并轉換為 unsigned char* 類型,由 sizeof() 指定要寫入的字節數。
一組數據的文件讀寫實例:
#include <iostream> #include <fstream> using namespace std;void myread(const char* fname) {ifstream file_read(fname, ios::binary);char c[30];if(file_read){file_read.read(c, 26*sizeof(char));c[26] = '\0';cout << c <<endl;} }void mywrite(const char* fname) {char c[30];for(int i = 0; i < 26; i++)c[i] = 'A' + i;ofstream file_write(fname, ios::binary);if(file_write)file_write.write(c, 26*sizeof(char)); }int main() {char fname[20] = "word.file";mywrite(fname);myread(fname);return 0; }?
9.4.2 隨機訪問文件
順序訪問文件不適合快速訪問的應用程序。為了增加程序的靈活性和快捷性,立即找到特定記錄信息,C++ 提供了隨機移動文件訪問指針的函數,可以移動輸入/輸出流中的文件指針,從而對文件數據進行隨機讀寫。
獲得和設置流指針
所有輸入/輸出流對象都有至少一個流指針:
ifstream, 類似 istream,有一個被稱為 get pointer 的指針,指向下一個將被讀取的元素。
ofstream,類似 ostream,有一個指針 put pointer ,指向寫入下一個元素的位置。
fstream, 類似 iostream,同時繼承了get 和 put。
tellg() 和 tellp()
這兩個成員函數不用傳入參數,返回 pos_type 類型的值(根據 ANSI-C++ 標準) ,就是一個整數,代表當前 get 流指針的位置 (用 tellg)?或 put 流指針的位置(用 tellp)。
C++ 支持的文件流中,通過文件指針方式從相應的文件位置進行讀寫。其中的 get() 函數指針用于指出下一次輸入操作的位置;put() 函數指針用于指出下一個輸出操作的位置。每當發生一次讀寫操作時,相應的 get() 函數指針或 put() 函數指針會自動連續地增加。另外也可以使用?seekg() 函數和 seekp() 函數迅速定位,找到指定的位置訪問文件。函數原型如下:
ostream& seekp(streamoff off, ios::seek_dir dir); istream& seekg(streamoff off, ios::seek_dir dir);函數 seekp() 用于輸出文件,將相應的文件指針 get 從 dir 的位置移動 off 個字節;函數 seekg() 用于輸入文件,將相應的文件指針 put 從 dir 的位置移動到 off 個字節。
其中類型 streamoff 相當于 long 類型,定義文件指針要偏移的位置范圍。參數 dir 表示文件指針的起始位置,off 表示相對于這個位置移動的位偏移量。dir 取值如下:
ios::beg:從文件頭開始,此時off取值為正。 ios::cur:從文件當前位置開始,此時off取值為負。 ios::end:從文件末尾開始,此時off取值可正可負。獲得一個二進制文件的大小:
#include <iostream.h> #include <fstream.h>const char * filename = "test.txt";int main() {long l,m;ifstream in(filename, ios::in|ios::binary);l = in.tellg();in.seekg (0, ios::end);m = in.tellg();in.close();cout << "size of " << filename;cout << " is " << (m-l) << " bytes.\n";return 0; }隨機訪問文件實例:
#include <iostream> #include <fstream> using namespace std;void myread(const char* fname) {ifstream file_read(fname, ios::binary);char c[10];if(file_read){file_read.seekg(1, ios::beg); //從開始位置移動1位file_read.get(c[0]); //讀取一個字符file_read.seekg(2, ios::cur); //從當前位置移動2位file_read.get(&c[1], 4*sizeof(char));//一次讀取1個字符,連續讀取3次,最后一個'\0'cout << c;} }void mywrite(const char* fname) {char c[30];for(int i = 0; i < 26; i++)c[i] = 'A' + i;ofstream file_write(fname, ios::binary);if(file_write)file_write.write(c, 26*sizeof(char)); }int main() {char fname[20] = "word.file";mywrite(fname);myread(fname);cout <<endl;return 0; }?
總結
以上是生活随笔為你收集整理的C++学习笔记:(九)输入/输出流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C/C++中输入带空格的字符串 str
- 下一篇: 美团网上笔试题