C++PrimerPlus學習之輸入,輸出和檔案
流和緩衝區
- C++程式把輸入和輸出看作位元組流。輸入時,程式從輸入流中抽取位元組;輸出時,程式將位元組插入到輸出流中。
- 流充當了程式和流源或流目標之間的橋樑。C++程式只是檢查位元組流,而不需要知道位元組來自何方和去向何處。
- 使用緩衝區可以更高效地處理輸入和輸出。
- 處理輸入時,緩衝區通常從磁碟讀取大量資訊,然後每次從緩衝區裡讀取一個位元組。
- 處理輸出時,程式首先先填滿緩衝區,然後將整塊資料傳輸給硬碟,並清空緩衝區,以備下一批輸出使用。這被稱為重新整理緩衝區。但如果是通過鍵盤來輸入的話,在這種情況下,C++程式通常在使用者按下回車鍵時重新整理輸入緩衝區。
- 流檔案的關係圖
streambuf
ios_base
類表示流的一般特徵,如是否可讀取,是二進位制還是文字流等ios
類基於ios_base
,其中包括了一個指向streambuf
物件的指標成員ostream
類是從ios
類派生而來的,提供了輸出方法istream
類是從ios
類派生而來的,提供了輸入方法iostream
類是基於istream
和ostream
類的,因此繼承了輸入方法和輸出方法fstream
類是從iostream
類派生而來的,而後者基於istream
和ostream
類,因此它繼承了它們的方法
iostream
cin
物件對應於標準輸入流。在預設情況下,這個流被關聯到標準輸入裝置(通常是鍵盤)。wcin
物件與此類似,但處理的是wchar_t
型別。cout
物件與標準輸出流對應。在預設情況下,這個流被關聯到標準輸出裝置(通常為顯示器)。wcout
物件與此類似。cerr
物件與標準錯誤流相對應,可用於顯示錯誤訊息。在預設情況下,這個流被關聯到標準輸出裝置(通常為顯示器)。這個流沒有被緩衝,這意味著資訊將被直接傳送到螢幕,而不會等到緩衝區填滿或新的換行符。wcerr
物件與此類似。clog
物件也對應著標準錯誤流。在預設情況下,這個流被關聯到標準輸出裝置(通常為顯示器)。這個流被緩衝。wclog
使用cout
進行輸出
-
write()
- 模板原型
basic_ostream<charT,traits>& wirte(const char_type *s,streamsize n);
write()
的第一個引數提供了要顯示的字串的地址,第二個引數指出要顯示多少個字元。使用cout
呼叫write()
時,將呼叫char
具體化,因此返回型別為ostream &
- 一個例子
#include<bits/stdc++.h> using namespace std; int main() { const char* one="helloworld"; const char* two="guoshen"; int len=strlen(one); for(int i=1;i<=len;i++) { cout.write(one,i); cout<<endl; } cout.write(one,len+8); }
- 注意,
write()
方法並不會在遇到空字元時自動停止列印字元。
- 模板原型
-
進位制轉換(
hex(),dec(),oct()
)#include<bits/stdc++.h> using namespace std; int main() { int a=1234; cout<<hex<<a<<endl;//16進位制 cout<<oct<<a<<endl;//8進位制 cout<<dec<<a<<endl;//10進位制 } /* output: 4d2 2322 1234 */
-
int width()
和int width(int i)
-
前者返回欄位寬度的當前設定,後者設定字元寬度為i個空格,並返回以前的欄位寬度
-
調整欄位寬度
-
只會影響下一個專案,然後欄位寬度將恢復為預設值
-
預設是右對齊
-
一個例子
#include<bits/stdc++.h> using namespace std; int main() { cout<<'#'; cout.width(12); cout<<12<<'#'<<24<<"#\n"<<endl; } /* output: # 12#24# */
-
-
fill(char a)
-
填充未被使用的欄位
-
一直有效,直到被更改
-
一個例子
#include<bits/stdc++.h> using namespace std; int main() { cout<<'#'; cout.width(12); cout.fill('*'); cout<<12<<'#'<<24<<"#\n"<<endl; } /* output: #**********12#24# */
-
-
precision(int i)
-
設定浮點數的顯示精度為i
-
預設精度為6位(但末尾的0將不顯示)
-
一直有效,直到被更改
-
一個例子
#include<bits/stdc++.h> using namespace std; int main() { double pi=acos(-1.0); cout<<pi<<endl; cout.precision(2); cout<<pi<<endl; cout.precision(10); cout<<pi<<endl; } /* output: 3.14159 3.1 3.141592654 */
-
-
setf()
- 第一個原型
fmtflags setf(fmtflags);
fmtflags
是bitmask
型別的typdef
名,用於儲存格式標記。- 在
ios_base
類中定義。 - 引數是一個
fmtflags
值,指出要設定哪一位。返回值是型別為fmtflags
的數字,指出所有標記以前的設定。如果打算以後恢復原始設定,則可以儲存這個值。 - 格式常量表
式 常量 os_base::boolalpha 輸入和輸出bool值,可以為true或false os_base::showbase 對於輸出,使用C++基數字首(0,0x) os_base::showpoint 顯示末尾的小數點 os_base::uppercase 對於16進位制輸出,使用大寫字母,E表示法 os_base::showpos 在正數前面加+ - 第一個原型
-
-
第二個原型
fmtflags setf(fmtflags,fmtflags);
-
第一個引數和以前一樣,也是一個包含了所需的
fmtflags
值。第二個引數指出要清除第一個引數中的哪一位。 -
呼叫
setf()
的效果可以通過unsetf()
消除 -
引數表
-
第二個引數 | 第一個引數 | 含義 |
---|---|---|
ios_base::basefield | ios_base::dec | 使用基數10 |
ios_base::basefield | ios_base::oct | 使用基數8 |
ios_base::basefield | ios_base::hex | 使用基數16 |
ios_base::floatfield | ios_base::fixed | 使用定點計數法 |
ios_base::floatfield | ios_base::scientific | 使用科學計數法 |
ios_base::adjustfield | ios_base::left | 使用左對齊 |
ios_base::adjustfield | ios_base::right | 使用右對齊 |
ios_base::adjustfield | ios_base::internal | 符號或基數字首左對齊,值右對齊 |
使用cin
進行輸入
- 流狀態
成員 | 描述 |
---|---|
eofbit | 如果到達檔案尾,則設定為1 |
badbit | 如果流被破壞,則設定為1;例如,檔案讀取錯誤 |
failbit | 如果輸入操作未能讀取預期的字元或輸出操作沒有寫入預期的字元,則設定為1 |
goodbit | 另一種表示0的方法 |
good() | 如果流可以使用(所有的位都被清除),則返回true |
eof() | 如果eofbit被設定,則返回true |
bad() | 如果badbit被設定,則返回true |
fail() | 如果badbit或failbit被設定,則返回true |
rdstate () | 返回流狀態 |
exceptions () | 返回一個位掩碼,指出哪些標記導致異常被引發 |
exceptions(isostate ex) | 設定哪些狀態將導致clear()引發異常;例如,如果ex是eofbit,則如果eofbit被設定,clear()將引發異常 |
clear(iostate s) | 將流狀態設定為s;s的預設值為0(goodbit);如果(restate()&exception())!=0,則引發異常basic_ios::failure |
setstate(iostate s) | 呼叫clear(rdstate() | s).這將設定與s中設定的位對應的流狀態位,其他流狀態位保持不變 |
- I/O和異常
-
修改流狀態涉及
clear()
和setstate()
,這都將會使用clear()
,修改流狀態後,clear()
方法將當前的流狀態與exceptions()
返回的值進行比較。如果在返回值(exceptions()
)中某一位被設定,而在當前狀態中對應位也被設定,則clear()
將引發ios_base::failure
異常。 -
exceptions()
的預設設定為goodbit
,也就是沒有引發異常,但過載的exceptions(iostate)
函式使得能夠控制其行為:cin.exceptions(badbit)
-
一個例子
#include<bits/stdc++.h> using namespace std; int main() { cin.exceptions(ios_base::failbit); int x; int sum=0; try { while(cin>>x) { sum+=x; } } catch(ios_base::failure &bf) { cout<<bf.what()<<endl; } cout<<sum<<endl; } /* input: 10 20 30 pi output: basic_ios::clear 60 */
-
- 一些函式
get()
和getline()
和ignore()
-
get()
會將換行符留在輸入流中,接下來的輸入操作首先會是換行符,而getline()
不會 -
ignore()
的原型為istream & ignore(int =1,int =EOF)
該函式接受兩個引數,一個是數字,指定要讀取的最大字元數;另一個是字元,用作輸入分界符。下面的函式呼叫讀取並丟棄接下來的255個字元或直到到達第一個換行符
cin.ignore(255,'\n');
-
檔案的輸入和輸出
- 二進位制檔案
-
二進位制檔案比較精確
-
要使用成員函式
read()
和write()
-
適用於結構體和不使用虛擬函式的類。
-
一個例子
#include<bits/stdc++.h> using namespace std; struct planet { char name[20]; double populatition; double g; }; const char *file="planets.dat"; inline void eatline(){while(cin.get()!='\n')continue;} int main() { planet p1; cout<<fixed<<right; ifstream fin; fin.open(file,ios_base::in|ios_base::binary); if(fin.is_open()) { while(fin.read((char*)&p1,sizeof p1)) { cout<<setw(20)<<p1.name<<": " <<setprecision(0)<<setw(12)<<p1.populatition <<setprecision(2)<<setw(6)<<p1.g<<endl; } fin.close(); } //add new data ofstream fout(file,ios_base::out|ios_base::app|ios_base::binary); if(!fout.is_open()) { cerr<<"Can't open"<<file<<endl; exit(EXIT_FAILURE); } cin.get(p1.name,20); while(p1.name[0]!='\0') { eatline(); cin>>p1.populatition; cin>>p1.g; eatline(); fout.write((char*)&p1,sizeof p1); cin.get(p1.name,20); } fout.close(); //show data fin.clear(); fin.open(file,ios_base::in|ios_base::binary); if(fin.is_open()) { while(fin.read((char*)&p1,sizeof p1)) { cout<<setw(20)<<p1.name<<": " <<setprecision(0)<<setw(12)<<p1.populatition <<setprecision(2)<<setw(6)<<p1.g<<endl; } fin.close(); } cout<<"Done\n"; return 0; } /* 輸入請參照書本 */
-
隨機存取
seekg()
和seekp()
函式-
seekg()
的原型(seekp()
與之類似)basic_istream<charT,traits>& seekg(streamoff,ios_base::seekdir); basic_istream<charT,traits>& seekg(streampos);
第一個原型定位到離第二個引數指定的檔案位置特定距離(單位為位元組)的位置;第二個原型定位到離檔案開頭特定距離(單位為位元組)的位置
-
示例(假設
fin
是一個iftream
物件)fin.seekg(30,ios_base::beg);//30 bytes beyond the begining fin.seekg(-1,ios_base::cur);//back up one byte fin.seekg(0,ios_base::end);//go to the end of the file fin.seekg(112);//go to the 113 bytes
-
- 建立臨時檔案
tmpnam()
-
原型
char * tmpnam(char * pszName);
建立一個臨時檔名,將它放在
pszName
指向的C-風格字串中 -
常量
L_tmpnam
為檔名包含的最大字數,TMP_MAX
為該函式在不生成重複檔名的情況下最多可呼叫次數 -
一個例子
#include<bits/stdc++.h> using namespace std; int main() { char tmp[L_tmpnam]={'\0'}; for(int i=0;i<10;i++) { tmpnam(tmp); cout<<tmp<<endl; } }
-
-
核心格式化
-
C++庫提供了
sstream
族,它們使用相同的介面提供程式和string
物件之間的I/O。 -
標頭檔案
sstream
定義了一個從ostream
類派生而來的ostringstream
類。如果建立了一個ostringstream
物件,則可以將資訊寫入其中,它將儲存這些資訊。 -
ostringstream
類有一個名為str()
的成員函式,該函式返回一個被初始化為緩衝區內容的字串物件 -
一個例子
#include<bits/stdc++.h> using namespace std; int main() { ostringstream outstr; string tmp; getline(cin,tmp); int cap; cin>>cap; outstr<<"hello world "<<tmp<<" has a capacity of "<<cap<<endl; string result=outstr.str(); cout<<result<<endl; }
-
istringstream
類允許使用istream
方法族讀取istringstream
物件的資料,istringstream
物件可以使用string
物件進行初始化 -
一個例子
#include<bits/stdc++.h> using namespace std; int main() { istringstream in(string("guo shen is a cool boy!")); string tmp; while(in>>tmp) { cout<<tmp<<endl; } }
-
總之,
istringstream
和ostringstream
類使得能夠使用istream
和ostream
類的方法來管理儲存在字串中的字元資料。