8.1 IO類
IO庫型別和頭檔案
| 頭檔案 | 型別 |
|---|---|
| iostream | istream,wistream:從流讀取資料 ostream,wostream:向流寫入資料 iostream,wiostream:寫入流 |
| fstream | ifstream,wifstream:從檔案讀取資料 ofstream,wofstream:向檔案寫入資料 fstream,wfstream:讀寫檔案 |
| sstream | istringstream,wistringstream:從string讀取資料 ostringstream,wostringstream:向string讀取資料 stringstream,wstringstream:讀寫string |
為了支持使用寬字符的語言,標準庫定義了一組型別和物件來操作wchar_t型別的資料,寬字符版本的型別和函式的名字以W開始,
如wcin,wcout,wcerr
IO型別間的關系
概念上設備型別和字符大小都不會影響IO操作,
標準庫通過繼承機制來忽略差異,
繼承機制使我們可以宣告一個特定的類繼承自另一個類,通常將一個派生類(繼承類)物件當作其基類(所繼承的類)物件來使用,
ifstream和istringstream都繼承istream,
IO物件無拷貝或賦值
不能將形參或回傳型別設定為流型別,
進行IO操作的函式通常以參考方式傳遞和回傳流,
讀寫IO物件會改變其狀態,因此參考不能是const,
條件狀態
見表8.2
int ival;
cin >> ival;
如果輸入錯誤,cin會進入錯誤狀態,其后續的IO都會失敗,
因此通常使用回圈檢查>>運算式回傳的流的狀態
while(cin >> word)
//ok:讀操作成功
查詢流狀態
將流作為條件,只能知道是否有效,無法知道為什么失敗,
IO庫定義了iostate型別,提供了表達流狀態的功能,
定義了4個iostate型別的constexpr值,表示特定的位模式,
這些值用來表示特定的IO條件,可以和位運算子一起使用一次性檢測或設定多個標志位,
badbit:表示系統級錯誤,如不可恢復的讀寫錯誤,一旦被置為,流無法使用,
failbit:表示可恢復的錯誤,
eofbit:到達檔案未和failbit一起置位,
goodbit:為0時表示未發生錯誤,
如果badbit、failbit、eofbit任一個被置位,則檢測流狀態的條件就會失敗,
還定義了一組函式來查詢標志位的狀態
good:所有錯誤都沒置位的情況下回傳true
bad、fail、eof:在對于錯誤位置回傳true
badbit被置為時,fail也回傳true
使用good或fail時確定流總體狀態的方法,
將流當作條件使用的代碼等價于!fail()
管理條件狀態
流物件的rdstate成員回傳一個iostate值,對應流的當前狀態,
setstate將給的條件位置位,表示發生了對應錯誤,
clear有兩個版本(1、不接受引數 2、接受iostate引數)
clear不接受引數的版本復位所有的錯誤標志位,
執行clear(),good會回傳true
auto old_state = cin.rdstate();
cin.clear();
process_input(cin); //使用cin
cin.setstate(old_state); //將cin置為原有狀態
帶引數的claer接受一個iostate值,表示流的新狀態,
//復位failbit和badbit,其他保持不變
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit);
管理輸出緩沖
每個輸出流都管理一個緩沖區,保存程式讀寫的資料,
有了緩沖機制,作業系統可以將程式的多個輸出操作組合成單一的系統級寫操作,提升性能,
緩沖重繪的原因有:
1、程式正常結束
2、緩沖區滿
3、使用endl來重繪
4、使用unitbuf設定流的內部狀態,清慷訓沖區,默認情況下,對cerr時設定unitbuf的,因此寫到cerr的內容立即重繪
5、一個輸出流被關聯到另一個流,當讀寫被關聯的流時,關聯到的流緩沖區會被重繪,
重繪輸出緩沖區
使用flush endl ends
cout << "hi!" << endl; //輸出一個換行,重繪緩沖區
cout << "hi!" << flush; //直接重繪,不加任何字符
cout << "hi!" << ends; //輸出一個空字符,然后重繪
unitbuf操縱符
如果想每次操作后都重繪,使用unitbuf操縱符,告訴流在接下去每次操作后都進行flush,nounitbuf則重置,使流回復到正常的緩沖區重繪機制,
cout << unitbuf; //所有輸出操作后都立即重繪
//任何輸出都不緩沖
cout << nounitbuf; //恢復
關聯輸入和輸出流
當一個輸入流被關聯到一個輸出流時,任何試圖從輸入流讀取資料的操作都會先重繪關聯的輸出流,標準庫將cout和cin關聯在一起,
cin >> ival; //cout的緩沖區被重繪
互動式系統應該關聯輸入流和輸出流,意味著所有的輸出都會在讀操作前被列印出來,
tie有兩個多載版本,一個無引數,回傳指向輸出流的指標,如果本物件關聯到前一個輸出流,則回傳的是指向這個流的指標;如果物件未關聯流,則回傳空指標,
第二個版本接受一個指向ostream的指標,將自己關聯到ostream,即x.tie(&o)將流x關聯到輸出流o,
cin.tie(&cout); //將cin和cout關聯
//old_tie指向當前
ostream *old_tie = cin.tie(nullptr); //cin不在與其他流關聯
cin.tie(&cerr); //cin與cerr關聯,
cin.tie(old_tie); //重建cin和cout之間的關聯
8.2 檔案輸入輸出
頭fstream定義了ifstream來讀取,ofstream來寫入,fstream來讀寫檔案,
可以用<< >>來讀寫,還可以用getline從一個ifstream讀取資料,
fstream還有一些特有的操作見表8.3
fstream fstrm; //創建一個
fstream fstrm(s); //創建并指向
fstream fstrm(s, mode); //指定一個模式打開
fstrm.open(s);
fstrm.close();
fstrm.is_open(); //回傳一個布林值
使用檔案流物件
創建檔案流物件時,可以提供檔案名(open自動呼叫):
ifstream in(ifile); //關聯檔案并打開
ofstrem out; //未關聯檔案
用fstream代替iostream&
猶豫繼承機制,在接受iostream型別參考(指標)引數的函式,可以用fstream(sstream)來呼叫,
ifstream input(argv[1]);
Sakes_data total;
read(input, total);
//read函式宣告
std::istream &read(std::istream&, Sales_data&);
//可以用cin
read(cin, total);
read函式形參是istream&,但可以傳遞fstream物件,
成員函式open和close
如果定義了一個空檔案,使用open關聯
ifstream in(ifile);
ofstream out;
out.open(ifile + ".copy");
如果open失敗,failbit會置位,
通常open后檢查一下
if(out)
檔案關閉后,可以打開新檔案
in.close();
in.open(ifile + "2");
自動構造和析構
如果ifstream用在回圈中,作為區域變數,每次回圈都要創建和銷毀,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,從末尾追加
6、ate和binary可以用于任意型別的檔案流物件,且和其他模式組合使用
以out模式打開會丟棄已有資料
默認情況下打開ofstream時,檔案內容會清空,
如果不清空就要用app模式
//以及幾種模式都會隱含截斷檔案
ofstream out("file");
ofstream out("file", ofstream::out);
ofstream out("file", ofstream::out | ofstream::trunc);
//保留內容
ofstream out("file", ofstream::app);
ofstream out("file", ofstream::out | ofstream::app);
每次呼叫open時都會確定檔案模式
ofstream out;
out.open("scratchpad"); //隱含截斷
8.3 string流
sstream頭檔案定義了string流,
特有操作:
sstream strm;
sstream strm(s); //保存string s的拷貝
strm.str(); //回傳strm保存的string拷貝
strm.str(s); //將string s拷貝到strm中,回傳void
使用istringstream
需要對整行文本處理時,通常用istringstream,
假設有電話資料
morgan 0123456 5498456
drew 9535873
lee 60955481 25484535 154987
//定義保存的結果
struct PersonInfo{
string name;
vector<string> phones;
}
//保存
string line, word;
vector<PersonInfo> people;
while (getline(cin, line))
{
PersonInfo info;
istringstream record(line); //讀取當前行
record >> info.name; //讀取名字
while (record >> word) //讀取電話號碼
info.phones.push_back(word);
people.push_back(info);
}
使用ostringstream
對于上一個例子,想要逐個驗證號碼,然后輸出到一個新檔案中,可以先把輸出寫入到ostringstream
for (const auto &entry : people)
{
ostringstream formatted, badNums;
for (const auto &nums : entry.phones)
{
if(!valid(nums))
{
badNums << " " << nums; //將數的字串存入badNums
}
else
{
formatted << " " << format(nums); //將格式化的字串寫入formatted
}
}
if (badNums.str().empty()) //如果沒有錯誤
{
os << entry.name << " " << formatted.str() << endl;
}
else
{
cerr << "input error:" << entry.name
<< "invalid number(s)" << badNums.str() << endl;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/231713.html
標籤:C++
上一篇:c++11-17 模板核心知識(十四)—— 決議模板之依賴型模板名稱 Dependent Names of Templates(.template/->template/::template)
下一篇:圖 Graph
