IO庫----IO類,檔案輸入輸出,string流
一、IO類
1、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開始。
類fstream和stringstream都是繼承自類iostream的。輸入類都繼承自istream,輸出類都繼承自ostream。因此,可以在istream物件上執行的操作,也可以在ifstream或istringstream物件上執行。繼承自ostream的輸出類也是類似的情況。
2、IO物件無拷貝或賦值
我們不能拷貝或對IO物件賦值。進行IO操作的函式通常以引用方式傳遞和返回值。讀寫一個IO物件會改變其狀態,因此傳遞和返回的引用不能是const的。
3、條件狀態
IO類定義的一些函式和標誌:
條件狀態 | 說明 |
strm::iostate | strm是一種IO型別。iostate是一種機器相關的型別,提供了表達條件狀態的完整功能。 |
strm::badbit | 用來指出流以崩潰。 |
strm::failbit | 用來指出一個IO操作失敗了。 |
strm::eofbit | 用來指出流到達了檔案結束。 |
strm::goodbit | 用來指出流未處於錯誤狀態。此值保證為0。 |
s.eof() | 若流的eofbit置位,則返回true。 |
s.fail() | 若流的failbit或badbit置位,則返回true。 |
s.bad() | 若流的badbit置位,則返回true。 |
s.good() | 若流s處於有效狀態,則返回true。 |
s.clear() | 將流中所有條件狀態復位,將流的狀態設定為有效。返回void。 |
s.clear(flags) | 根據給定的flags標誌位,將流s中對應條件狀態位復位。flags的型別為strm::iostate。返回void。 |
s.setstate(flags) | 根據給定的flags標誌位,將流s中對應條件狀態位置位。flags的型別為strm::iostate。返回void。 |
s.rdstate() | 返回流s的當前條件狀態,返回值型別為strm::iostate。 |
badbit表示系統級錯誤,如不可恢復的讀寫錯誤。通常情況下,一旦badbit被置位,流就無法再使用了。
在發生可恢復錯誤後,failbit被置位,如期望讀取數值卻讀出一個字元等錯誤。這種問題通常是可以修正的,流還可以繼續使用。
如果到達檔案結束位置,eofbit和failbit都會被置位。
goodbit的值為0,表示流未發生錯誤。
如果badbit、failbit、eofbit任一個被置位,則檢測流狀態的條件會失敗。
4、管理輸出緩衝
1)緩衝區
每個輸出流都管理一個緩衝區,用來儲存程式讀寫的資料。有了緩衝機制,作業系統就可以將程式的多個輸出操作組合成單一的系統寫操作。由於裝置的寫操作可能很耗時,允許作業系統將多個輸出操作組合為單一的裝置寫操作可以帶來很大的效能提升。
導致緩衝重新整理(即,資料真正寫到輸出裝置或檔案)的原因有很多:
a、程式正常結束,作為main函式的return操作的一部分,緩衝重新整理被執行。
b、緩衝區滿時,需要重新整理緩衝而後新的資料才能繼續寫入緩衝區。
c、我們可以使用操縱符endl來顯示重新整理緩衝區。
d、在每個輸出操作之後,我們可以用操縱符unitbuf設定流的內部狀態,來情況緩衝區。預設情況下,對cerr是設定unitbuf的,因此寫到cerr的內容都是立即重新整理的。
e、一個輸出流可能被關聯到另一個流。在這種情況下,當讀寫被關聯的流時,關聯到流的緩衝區會被立即重新整理。例如,預設情況下,cin和cerr都關聯到cout。因此。讀cin或寫cerr都會導致cout的緩衝區被重新整理。
2)重新整理輸出緩衝
我們已經使用過操縱符endl,它完成換行並重新整理緩衝區的工作。IO庫還有兩個類似的操縱符:flush和ends。flush重新整理緩衝區,但不輸出額外的字元;ends想緩衝區插入一個空字元,然後重新整理緩衝區。
1 #include <iostream> 2 #include <string> 3 #include <fstream> 4 #include <sstream> 5 6 int main() 7 { 8 std::cout << "hi!" << std::endl; 9 std::cout << "hi!" << std::ends; 10 std::cout << "hi!" << std::flush; 11 return 0; 12 }View Code
3)unitbuf操縱符
如果想在每次輸出操作後都重新整理緩衝區,我們可以使用unitbuf操縱符。它告訴流在接下來的每次寫操作之後都進行一次flush操作。而nounitbuf操縱符則重置流,使其恢復使用正常的系統管理的緩衝區重新整理機制:
1 #include <iostream> 2 3 int main() 4 { 5 std::cout << std::unitbuf; 6 // 任何輸出都立即輸出,無緩衝 7 std::cout << std::nounitbuf; 8 return 0; 9 }View Code
注意:如果程式異常中止,輸出緩衝區是不會被重新整理的。當一個程式崩潰後,它所輸出的資料很可能停留在輸出緩衝區中等待列印。
4)關聯輸入和輸出流
當一個輸入流被關聯到一個輸出流時,任何試圖從輸入流讀取資料的操作都會先重新整理關聯的輸出流。
tie有兩個過載的版本:一個版本不帶引數,返回指向輸出流的指標。如果本物件當前關聯到一個輸出流,則返回的就是指向這個流的指標,如果物件未關聯到流,則返回空指標。tie的第二個版本接受一個指向ostream的指標,將自己關聯到此ostream。即,x.tie(&o)將流x關聯到輸出流o。
我們既可以把一個輸入流關聯到另一個輸出流,也可以將一個輸出流關聯到另一個輸出流:
1 #include <iostream> 2 3 int main() 4 { 5 std::cin.tie(&std::cout); // 標準庫將cin和cout關聯在一起 6 // old_tie指向當前關聯到cin的流 7 std::ostream *old_tie = std::cin.tie(nullptr); // cin不再與其他流關聯 8 std::cin.tie(&std::cerr); // cin與cerr關聯 9 std::cin.tie(old_tie); // cin恢復正常關聯 10 return 0; 11 }View Code
每個流同時最多關聯到一個流,但多個流可以同時關聯到同一個ostream。
二、檔案輸入輸出
標頭檔案fstream定義了三個型別來支援檔案IO:ifstream從一個給定檔案讀取資料,ofstream向一個給定檔案寫入資料,fstream可以讀寫檔案。這些型別提供的操作和物件cin和cout的操作一樣。
fstream特有的操作:
操作 | 說明 |
fstream fstrm; | 建立一個未繫結的檔案流。fstream是標頭檔案fstream中定義的一個型別 |
fstream fstrm(s); | 建立一個fstream,並開啟名為s的檔案。s可以是string型別,或者是一個指向C風格字串的指標。這些建構函式都是explicit的。預設的檔案模式mode依賴於fstream的型別 |
fstream fstrm(s, mode); | 與前一個建構函式類似,但按指定mode開啟檔案 |
fstrm.open(s) | 開啟名為s的檔案,並將檔案與fstrm繫結,s可以是string型別或者是一個指向C風格字串的指標。預設的檔案模式mode依賴於fstream的型別。返回void |
fstrm.close() | 關閉與fstrm繫結的檔案。返回void |
fstrm.is_open() | 返回一個bool值,指出與fstrm關聯的檔案是否成功開啟且尚未關閉 |
1、使用檔案流物件
建立檔案流物件時,我們可以提供檔名(可選的)。如果提供了一個檔名,則open會自動呼叫。
1 #include <iostream> 2 #include <fstream> 3 #include <string> 4 5 int main() 6 { 7 std::ofstream out("hello.txt"); 8 out << "QAQ" << std::endl; 9 out.close(); 10 std::ifstream in; 11 in.open("hello.txt"); 12 if (in.is_open()) 13 { 14 std::string str; 15 in >> str; 16 in.close(); 17 std::cout << str << std::endl; 18 } 19 return 0; 20 }View Code
1)成員函式open和close
如果我們定義了一個空檔案流物件,可以隨後呼叫open來將它與檔案關聯起來。如果呼叫open失敗,failbit會被置位。因為呼叫open可能失敗,進行open是否成功的檢測通常是很有必要的。
1 #include <iostream> 2 #include <fstream> 3 #include <string> 4 5 int main() 6 { 7 std::ifstream in; 8 in.open("hello.txt"); 9 if (in) // 檢測open是否成功 10 { 11 std::string str; 12 in >> str; 13 in.close(); 14 std::cout << str << std::endl; 15 } 16 return 0; 17 }View Code
一旦一個檔案流已經開啟,它將保持與對應檔案的關聯。實際上,對一個已經開啟的檔案流呼叫open會失敗,炳輝導致failbit被置位。隨後的試圖使用檔案流的操作都會失敗。為了將檔案流關聯到另外一個檔案,必須首先關閉已經關聯的檔案。一旦檔案成功關閉,我們可以開啟新的檔案。
1 #include <iostream> 2 #include <fstream> 3 #include <string> 4 5 int main() 6 { 7 std::ifstream in; 8 in.open("hello.txt"); 9 in.close(); 10 in.open("hello2.txt"); 11 if (in) // 檢測open是否成功 12 { 13 std::string str; 14 in >> str; 15 in.close(); 16 std::cout << str << std::endl; 17 } 18 return 0; 19 }View Code
2)、自動構造和析構
當一個fstream 物件離開其作用域時會被銷燬,close()會自動被呼叫。
2、檔案模式
每個流都有一個關聯的檔案模式,用來指出如何使用檔案。
模式 | 說明 |
in | 以讀方式開啟 |
out | 以寫方式開啟 |
app | 每次寫操作均定位到檔案末尾 |
ate | 開啟檔案後立即定位到檔案末尾 |
trunc | 截斷檔案 |
binary | 以二進位制方式進行IO |
無論用哪種方式開啟檔案,我們都可以指定檔案模式,呼叫open開啟檔案時可以,用一個檔名初始化流來隱式開啟檔案時也可以。指定檔案模式有以下限制:
a、只可以對ofstream和fstream物件設定out模式
b、只可以對ifstream和fstream物件設定in模式
c、只有當out也被設定時才可以設定trunc模式
d、只要trunc沒被設定,就可以設定app模式。在app模式下,即使沒有顯示指定out模式,檔案也總是以輸出方式開啟
e、預設情況下,即使我們沒有指定trunc,以out模式開啟的檔案也會被截斷。為了保留以out模式開啟的檔案的內容,我們必須同時指定app模式,這樣只會把將資料追寫到檔案末尾;或者同時指定in模式,即開啟檔案同時進行讀寫操作
f、ate和binary模式可用於任何型別的檔案流物件,且可以與其他任何檔案模式組合
每個檔案流類型別都定義了一個預設的檔案模式,當我們未指定檔案模式時,就使用此預設模式。與ifstream關聯的檔案預設以in模式開啟;與ofstream關聯的檔案預設以out模式開啟;與fstream關聯的檔案預設以in和out模式開啟。
預設情況下,當我們開啟一個ofstream時,檔案的內容會被丟棄。阻止一個ofstream情況給定檔案內容的方法是同時指定app模式。
對於一個給定流,每當開啟檔案時,都可以改變其檔案模式。
1 #include <iostream> 2 #include <fstream> 3 #include <string> 4 5 int main() 6 { 7 std::ofstream o; // 以輸出模式開啟檔案並截斷檔案 8 std::ofstream out("hello.txt", std::ofstream::out|std::ofstream::app); // 保留了檔案的內容 9 out << "QAQ" << std::flush; 10 out.close(); 11 std::ifstream in; 12 in.open("hello.txt", std::ifstream::in); 13 if (in) // 檢測open是否成功 14 { 15 std::string str; 16 in >> str; 17 in.close(); 18 std::cout << str << std::endl; 19 } 20 return 0; 21 }View Code
三、string流
sstream標頭檔案定義了三個型別來支援記憶體IO:istringstream從string讀取資料,ostringstream向string寫入資料,而stringstream既可以從string讀取資料也可向string寫資料。
sstream的三個型別的特有的操作:
操作 | 描述 |
sstream strm; | strm是一個未繫結的stringstream物件。sstream是標頭檔案sstream中定義的一個型別 |
sstream strm(s); | strm是一個sstream物件,儲存string s的一個拷貝。此建構函式是explicit的 |
strm.str() | 返回strm所儲存的string的拷貝 |
strm.str(s) | 將string s拷貝到strm中 |
1)使用istringstream
當我們的某些工作是對整行文字進行處理,而其他一些工作是處理行內的單個單詞時,通常可以使用istringstream。
1 #include <iostream> 2 #include <sstream> 3 #include <string> 4 5 int main() 6 { 7 std::istringstream in; 8 in.str("hello world 233"); 9 std::string word; 10 while (in >> word) 11 { 12 std::cout << word << std::endl; 13 } 14 return 0; 15 }View Code
2)使用ostringstream
當我們逐步構造輸出,希望最後一起列印時,ostringstream是很有用的。
1 #include <iostream> 2 #include <sstream> 3 #include <string> 4 5 int main() 6 { 7 std::ostringstream out; 8 out << "hello "; 9 out << "world "; 10 out << "233"; 11 std::cout << out.str() << std::endl; 12 return 0; 13 }View Code