標籤:標準庫 south tor 寫代碼 含義 開啟 二進位 each string
在寫代碼的過程中。我們最常做的事就是io操作,不管是對控制台,還是檔案。但一段時間不寫代碼就忘了,這裡理一下C++標準I/O庫的詳細類和操作。
C++的標準I/O庫包含我們常常使用的iostream,fstream。以及不太常常使用的stringstream。前兩者是對控制台和檔案的I/O操作,stringstream則能夠使用I/O操作對記憶體中的資料進行格式化操作。
C++的標準I/O操作相對與C來說,更加的簡明,安全,但運行效率會有所下降。
標準I/O庫類繼承體系
對於程式設計語言來說,在概念上,從控制終端、磁碟檔案以及記憶體中讀取資料都應該不影響I/O操作。C++為瞭解決支援不同裝置的字元流。通過物件導向的思想(廢話了,你要不用這個思想,你還是什麼C++)。通過繼承來實現一組類,分別處理控制終端、磁碟檔案,以及記憶體資料的I/O操作,是引用cplusplus官網關於輸入輸出資料流類繼承體系的關係圖,自己畫了一下,例如以下:
由能夠知道,I/O操作的基類是ios_base,各個類的用途例如以下:
- <iostream>
- istream 從流中讀取資料
- ostream 向流中寫資料
- iostream 對流進行讀寫操作。派生於istream和ostream
- <fstream>
- ifstream 從檔案裡讀取資料。派生於istream
- ofstream 向檔案裡寫資料,派生於ostream
- fstream 讀寫檔案, 派生於iostream
- <sstream>
- istringstream 讀取string對象。派生於istream
- ostringstream 寫string對象。派生於ostream
- stringstream 讀寫string對象,派生於iostream
C++標準對於I/O體系,定義了主要的流格式標誌(hex, dec,等),檔案開啟模式(in, out, bin等),流的狀態標誌(failbit等)。以及相關的函數等,例如以下在linux 下/usr/include/c++/4.6/bits/ios_base.h中關於這些標誌的枚舉定義:
enum _Ios_Fmtflags { _S_boolalpha = 1L << 0, _S_dec = 1L << 1, _S_fixed = 1L << 2, _S_hex = 1L << 3, _S_internal = 1L << 4, _S_left = 1L << 5, _S_oct = 1L << 6, _S_right = 1L << 7, _S_scientific = 1L << 8, _S_showbase = 1L << 9, _S_showpoint = 1L << 10, _S_showpos = 1L << 11, _S_skipws = 1L << 12, _S_unitbuf = 1L << 13, _S_uppercase = 1L << 14, _S_adjustfield = _S_left | _S_right | _S_internal, _S_basefield = _S_dec | _S_oct | _S_hex, _S_floatfield = _S_scientific | _S_fixed, _S_ios_fmtflags_end = 1L << 16 };
enum _Ios_Openmode { _S_app = 1L << 0, _S_ate = 1L << 1, _S_bin = 1L << 2, _S_in = 1L << 3, _S_out = 1L << 4, _S_trunc = 1L << 5, _S_ios_openmode_end = 1L << 16 };
enum _Ios_Iostate { _S_goodbit = 0, _S_badbit = 1L << 0, _S_eofbit = 1L << 1, _S_failbit = 1L << 2, _S_ios_iostate_end = 1L << 16 };
I/O流的狀態
為了更好地管理I/O操作。標準定義了一系列條件狀態標誌和函數,用來管理流對象。比方說是否可用,以及出現的錯誤。
對於流的狀態標誌是在ios_base類中進行定義的。所以流的狀態標誌管理適用於終端、檔案、string流對象。流的狀態定義為:ios_base::iostate,詳細有哪些值在ios_base中有定義,例如以下:
typedef _Ios_Iostate iostate; static const iostate badbit =_S_badbit; //流被破壞 (流本身的問題) static const iostate eofbit =_S_eofbit; //流已到結尾 static const iostate failbit =_S_failbit; //I/O失敗(操作本身上的邏輯問題) static const iostate goodbit =_S_goodbit; //好流...正常
為了管理上述的狀態標誌,標準在ios類中提供了下面函數來進行處理:
iostate rdstate() const //返迴流的條件狀態void clear(iostate __state = goodbit); //設定流的條件狀態,預設設定為正常void setstate(iostate __state); //設定流的狀態,和clear的差別:不會清除其它標誌bool good() const //流的是否正常bool eof() const //流是否到結尾bool fail() const //流是否I/O失敗bool bad() const //流是否被破壞
能夠通過以下代碼簡單進行狀態標誌的測試:
#include <iostream>using std::cout;using std::cin;using std::endl;int main(){ int a; cin>>a; cout<<"goodbit: "<<cin.good()<<" eofbit: "<<cin.eof()<<" failbit: "<<cin.fail()<<" badbit: "<<cin.bad()<<endl; cin>>a; cout<<"goodbit: "<<cin.good()<<" eofbit: "<<cin.eof()<<" failbit: "<<cin.fail()<<" badbit: "<<cin.bad()<<endl;}輸入1 a後顯示範範例如以下:
1goodbit: 1 eofbit: 0 failbit: 0 badbit: 0agoodbit: 0 eofbit: 0 failbit: 1 badbit: 0
由上可知非法輸入會導致I/O失敗,failbit流標誌生效。
檔案I/O操作
C++提供了ifstream和ofstream類負責檔案I/O的讀和寫操作。類fstream負責檔案的讀寫。繼承關係文章開頭已說明。
前面說了,在ios_base基類中定義了檔案開啟模式,
typedef _Ios_Openmode openmode;/// Seek to end before each write.static const openmode app = _S_app;/// Open and seek to end immediately after opening.static const openmode ate = _S_ate;/// Perform input and output in binary mode (as opposed to text mode).static const openmode binary = _S_bin;/// Open for input. Default for @c ifstream and fstream.static const openmode in = _S_in;/// Open for output. Default for @c ofstream and fstream.static const openmode out = _S_out;/// Open for input. Default for @c ofstream.static const openmode trunc = _S_trunc;
詳細含義例如以下:
- ios_base::in 僅僅讀開啟
- ios_base::out 僅僅寫開啟
- ios_base::app 每次寫之前移到檔案尾
- ios_base::ate 開啟檔案後立馬定位到檔案尾
- ios_base::trunc 開啟檔案並清空檔案流
- ios_base::binary 以二進位模式進行讀寫操作
以ifstream方式開啟檔案,預設開啟檔案為in。以ofstream方式開啟,預設開啟檔案為out | trunc。fstream是預設以in | out方式開啟。假設要以ofstream開啟檔案。且同一時候儲存檔案的資料。僅僅能通過顯示的指定app開啟模式。
例如以下是fstream標頭檔裡的open函數原型:
ifstreamvoid open(const char* __s, ios_base::openmode __mode = ios_base::in)ofstreamvoid open(const char* __s, ios_base::openmode __mode = ios_base::out | ios_base::trunc)fstreamvoid open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out)
假設fstream開啟檔案是out,也會清空檔案裡資料。
當檔案以out方式開啟(不包括in模式)時,假設檔案不存在,會建立一個新檔案。
istream,ostream類中都定義了用於輸入輸出的演算法提取器(Arithmetic Extractor):‘>>‘和‘<<’操作符,能夠提取內建資料類型的資料,比如,輸入操作符>>用於從流緩衝區streambuf中提取資料。並輸出到特定類型的變數中,比如istream標頭檔裡詳細定義例如以下:
//類內定義的,用於提取__istream_type& operator>>(bool& __n);__istream_type& operator>>(short& __n);__istream_type& operator>>(unsigned short& __n);__istream_type& operator>>(int& __n);__istream_type& operator>>(unsigned int& __n); __istream_type& operator>>(long& __n); __istream_type& operator>>(unsigned long& __n);__istream_type& operator>>(long long& __n);__istream_type& operator>>(unsigned long long& __n);__istream_type& operator>>(float& __f)__istream_type& operator>>(double& __f)__istream_type& operator>>(long double& __f)__istream_type& operator>>(void*& __p)//類外定義的,用於提取字元template<class _Traits> inline basic_istream<char, _Traits>&operator>>(basic_istream<char, _Traits>& __in, unsigned char& __c);template<class _Traits>inline basic_istream<char, _Traits>&operator>>(basic_istream<char, _Traits>& __in, signed char& __c);template<class _Traits>inline basic_istream<char, _Traits>&operator>>(basic_istream<char, _Traits>& __in, unsigned char* __s);template<class _Traits>inline basic_istream<char, _Traits>&operator>>(basic_istream<char, _Traits>& __in, signed char* __s);//string類中對輸入輸出operator的重載,使其能支援string類型的輸入輸出template<typename _CharT, typename _Traits, typename _Alloc>basic_istream<_CharT, _Traits>&operator>>(basic_istream<_CharT, _Traits>& __is, basic_string<_CharT, _Traits, _Alloc>& __str);template<typename _CharT, typename _Traits, typename _Alloc>inline basic_ostream<_CharT, _Traits>&operator<<(basic_ostream<_CharT, _Traits>& __os, const basic_string<_CharT, _Traits, _Alloc>& __str)
因為ifstream和ofstream派生於istream和ostream,所以fstream相同支援對檔案流filebuf的輸入輸出操作。比例如以下例通過輸入輸出操作符讀取檔案裡的資料:
#include <iostream>#include <fstream>#include <string>using namespace std;int main (){ string fileName = "test.txt"; ofstream outOpt(fileName.c_str(), ios_base::out | ios_base::trunc); if(!outOpt) {cout<<fileName<<" open failed..."<<endl;return -1; } outOpt<<"1 2 3 test text"; outOpt.close(); int a[3]; string b[2]; ifstream inOpt(fileName.c_str()); inOpt>>a[0]>>a[1]>>a[2]; //以空白符為分隔,從檔案緩衝區中讀取三個整數 inOpt>>b[0]>>b[1]; //以空白符為分隔,從檔案緩衝區讀取兩個string資料(string類型對operator <<進行類重載,所以能夠支援該操作) cout<<a[0]<<‘ ‘<<a[1]<<‘ ‘<<a[2]<<endl; cout<<b[0]<<‘ ‘<<b[1]<<endl; inOpt.close(); return 0;}
在檔案I/O的過程中,我們常常要處理的是按行對資料進行分析處理。istream類內定義了getline成員函數支援以特定的size或delimiter(默覺得分行符號)來提取定長或以特定分隔字元分開的資料。相同,string標頭檔裡也提供了getline全域函數,支援從istream類中。以特定的size或delimiter(默覺得分行符號)來提取資料。定義例如以下:
//istream類內定義,提供從輸入資料流中讀取一行資料到char*中__istream_type&getline(char_type* __s, streamsize __n, char_type __delim);//delim默覺得換行//string標頭檔定義(實際在basic_string.h中), 提供從輸入資料流中讀取一行資料到string中template<typename _CharT, typename _Traits, typename _Alloc>basic_istream<_CharT, _Traits>&getline(basic_istream<_CharT, _Traits>& __is, basic_string<_CharT, _Traits, _Alloc>& __str, _CharT __delim);//delim默覺得換行
使用例如以下:
#include <iostream>#include <fstream>#include <string>using namespace std;int main(){ ifstream inFile("test.txt"); if(!inFile) { cout<<"open test.txt failed..."<<endl; return -1; } char str1[256] = ""; inFile.getline(str1, 255, ‘\n‘); string str2; getline(inFile, str2); cout<<str1<<endl; cout<<str2<<endl;}
為了對檔案高效的操作時常要用到檔案流的定位功能。在ios_base類中。定義了例如以下的成員,
typedef _Ios_Seekdir seekdir;static const seekdir beg = _S_beg; //檔案流開頭static const seekdir cur = _S_cur; //當前所在檔案流的位置static const seekdir end = _S_end; //檔案流尾部
在istream中定義了tellg和seekg。ostream中定義了tellp和seekp,分別用來擷取當前操作所在檔案流的位置和進行檔案流的操作的定位。例如以下是通過seek操作來讀取整個檔案的內容到記憶體中:
#include <iostream>#include <fstream>int main (){ std::ifstream is ("test.txt", std::ios_base::binary); if (is) { //擷取檔案的大小 is.seekg (0, std::ios_base::end); //定位到檔案結尾 int length = is.tellg(); //擷取當前所在檔案流的位置,因為已經定位檔案結尾,所以是擷取檔案流大小 is.seekg (0, is.beg); //又一次將pos定位到檔案流開始 char * buffer = new char [length]; is.read (buffer,length); is.close(); // 輸出到螢幕 std::cout.write (buffer,length); delete[] buffer; } return 0;}
記憶體資料的 I/O操作
C++ I/O標準庫支援記憶體資料的I/O的操作,在sstream標頭檔裡定義了 istringstream,ostringstream。stringstream,僅僅須要將流與記憶體string對象綁定,就能夠進行I/O操作。上面三個I/O類分別用來讀取記憶體string對象到指定對象中,將指定對象中的資料寫寫入string對象。stringstream可用來讀寫string對象。
在上面I/O檔案操作一節已經說了在istream和ostream中定義的標準輸入輸出操作,由派生關係可知,對於istringstream,ostringstream相同能夠進行標準輸入輸出操作,對stringstream的stringbuf進行操作(與iostream的streambuf,fstream的filebuf類似,這裡暫不介紹)。對於stringstream類經常使用的操作例如以下:
stringstream ss(mystr)//建立ss對象,stream流中存放string類型的mystr的資料;ss.str() //返回ss流中的資料,傳回型別為string類型ss.str(mystr) //設定ss流中資料為mystr的內容
詳細使用例如以下:
#include <iostream>#include <sstream>#include <string>using namespace std;int main(){ istringstream istream; istream.str("1 2 3 hello"); int a, b, c; string str1; istream>>a>>b>>c>>str1; //從流中讀取資料到對象中(以空白符為切割符) cout<<a<<‘ ‘<<b<<‘ ‘<<c<<‘ ‘<<str1<<endl; ostringstream ostream; ostream<<a<<‘ ‘<<b<<‘ ‘<<c<<‘ ‘<<str1; //向流中寫資料 string out = ostream.str(); cout<<out<<endl;}
C++標準I/O庫:iostream, fstream, sstringstream