在C語言中,用printf和scanf進行輸入輸出,往往不能保證所輸入輸出的數據是可靠的安全的。在C++的輸入輸出中,編譯系統對數據類型進行嚴格的檢查,凡是類型不正確的數據都不可能通過編譯。因此C++的I/O操作是類型安全(type safe)的。 C++的I/O操作是可擴展的,不僅可以用來輸入輸出標準類型的數據,也可以用於用戶自定義類型的數據。C++對標準類型的數據和對用戶聲明類型數據的輸入輸出,採用同樣的方法處理。C++通過I/O類庫來實現豐富的I/O功能。
1. IO類
類 fstream 和 stringstream 都是繼承自類 iostream 的。輸入類都繼承自 istream,輸出類都繼承自 ostream。因此,可以在 istream 對象上執行的操作,也可在 istream 或 istringstream 對象上執行。繼承自 ostream 的輸出類也有類似情況。
istream(輸入流)類型,提供輸入操作。
ostream(輸出流)類型,提供輸出操作。
cin,一個 istream 對象,從標準輸入讀取數據。
cout,一個 ostream 對象,向標準輸出寫入數據。
cerr,一個 ostream 對象,通常用於輸出程序錯誤信息,寫入到標準錯誤。
clog,clog流對象也是標準錯誤流,它是console log的縮寫。它的作用和cerr相同,都是在終端顯示器上顯示出錯信息。區別:cerr是不經過緩衝區,直接向顯示器上輸出有關信息,而clog中的信息存放在緩衝區中,緩衝區滿後或遇endl時向顯示器輸出。
>>運算符,用來從一個 istream 對象讀取輸入數據。
<<運算符, 用來向一個 ostream 對象中寫入輸出數據。
getline 函數,從一個給定的 istream 讀取一行數據,存入一個給定的 string 對象中。
eof 函數,從輸入流讀取數據,如果到達文件末尾(遇文件結束符),eof函數值爲非零值(真),否則爲0(假)。
peak 函數,peek是“觀察”的意思,peek函數的作用是觀測下一個字符。其調用形式爲: c=cin.peek( ), 函數的返回值是指針指向的當前字符,但它只是觀測,指針仍停留在當前位置,並不後移。如果要訪問的字符是文件結束符,則函數值是EOF(-1)。
putback 函數,其調用形式爲 cin.putback(ch),其作用是將前面用get或getline函數從輸入流中讀取的字符ch返回到輸入流,插入到當前指針位置,以供後面讀取。
ignore 函數,其調用形式爲 cin.ignore(n, 終止字符),函數作用是跳過輸入流中n個字符,或在遇到指定的終止字符時提前結束(此時跳過包括終止字符在內的若干字符),也可以不帶參數或只帶一個參數,如:
cin.ignore() // n默認值爲1,終止字符默認爲EOF, 相當於ignore(1, EOF)
IO庫條件狀態
strm::iostate strm 是一種IO類型,iostate 是一種機器相關的類型,提供了表達條件狀態的完整功能
strm::badbit 用來指出流已崩潰
strm::failbit 用來指出一個IO操作失敗了
strm::eofbit 用來指出流到達了文件結束
strm::goodbit 用來指出流未處於錯誤狀態,此值保證爲0
s.eof() 若流 s 的 eofbit 置位,則返回 true
s.fail() 若流 s 的 failbit 或 badbit 置位,則返回 true
s.bad() 若流 s 的 badbit 置位,則返回 true
s.good() 若流 s 處於有效狀態,則返回 true
s.clear() 將流 s 中所有條件狀態復位,將流的狀態設置爲有效,返回void
s.clear(flags) 根據給定的flag標誌位,將流 s 中對應條件狀態復位,flags 的類型爲 strm::iostate。返回void
s.setstate(flags) 根據給定的flag標誌位,將流 s 中對應條件狀態置位,flags 的類型爲 strm::iostate。返回void
s.rdstate() 返回流 s 的當前條件狀態,返回值類型爲 strm::iostate
endl操作符: 換行並刷新緩衝區,cout << endl;
flush操作符: 刷新緩衝區,不輸出任何額外的字符,cout << flush;
ends操作符: 輸出一個空字符並刷新緩衝區,cout << ends;
unitbuf操作符: cout << unitbuf; // 所有輸出操作後都會立即刷新緩衝區,無緩衝。
nounitbuf操作符: cout << nounitbuf; // 重置流,恢復使用正常系統管理的緩衝區刷新機制
2. 文件輸入輸出
頭文件 fstream 定義了三個類型來支持文件IO: ifstream 從一個給定文件讀取數據, ofstream 向一個給定文件寫入數據,以及 fstream 可以讀寫給定文件。
fstream 特有的操作
fstream fstrm; 創建一個未綁定的文件流, fstream 是頭文件 fstream 中定義的一個類型
fstream fstrm(s); 創建一個 fstream, 並打開名爲 s 的文件, s 可以是 string 類型,或者指向字符串的指針。這些構造函數都是 explicit 的,默認文件模式依賴於 fstream 的類型
fstream fstrm(s, mode); 與前一個構造函數類似,但按指定 mode 打開文件
fstrm.open(s); 打開名爲 s 的文件, 並將文件與fstrm 綁定,s 可以是 string 類型,或者指向字符串的指針。這些構造函數都是 explicit 的,默認文件模式依賴於 fstream 的類型,返回 void
fstrm.close(); 關閉與 fstrm 綁定的文件,返回 void
fstrm.is_open(); 返回一個 bool 值,指出與 fstrm 關聯的文件是否成功打開且尚未關閉
文件模式(mode)
in 以讀方式打開
out 以寫方式打開,隱含截斷文件
app 每次寫操作前均定位到文件末尾,隱含輸出模式
ate 打開文件後立即定位到文件末尾
trunc 截斷文件
binary 以二進制方式進行IO
// example8_8.cpp 追加寫入文件示例
#include <iostream>
#include <fstream>
#include "Sales_data.h"
using namespace std;
int main(int argc, char *argv[])
{
if (argc != 3) {
cerr << "請給出輸入、輸出文件名" << endl;
return -1;
}
ifstream in(argv[1]);
if (!in) {
cerr << "無法打開輸入文件" << endl;
return -1;
}
ofstream out(argv[2], ofstream::app);
if (!out) {
cerr << "無法打開輸出文件" << endl;
return -1;
}
Sales_data total; // 保存當前求和結果的變量
if (read(in, total)) { // 讀入下一筆交易
Sales_data trans; // 保存下一條交易數據的變量
while (read(in, trans)) { // 讀入剩餘的交易
if (total.isbn() == trans.isbn()) { // 檢查isbn
total.combine(trans); // 更新變量total當前的值
} else {
print(out, total) << endl; // 輸出結果
total = trans; // 處理下一本書
}
}
print(out, total) << endl; // 輸出最後一條交易
} else { // 沒有輸入任何信息
cerr << "沒有數據" << endl; // 通知用戶
}
return 0;
}
3. string流
sstream 頭文件定義了三個類型來支持內存IO,istringstream 從 string 讀取數據,ostringstream向 string 寫入數據,stringstream 既可從 string 讀數據,也可向 string 寫數據,就像 string 是一個IO流一樣。
stringstream 特有的操作
sstream strm; strm 是一個未綁定的 stringstream 對象。stream 是頭文件 sstream 中定義的一個類型
sstream strm(s); strm 是一個 sstream 對象,保存 string s 的一個拷貝,此構造函數是 explicit 的
strm.str(); 返回 strm 所保存的 string 的拷貝
strm.str(s); 將 string s 拷貝到 strm 中,返回 void
// example8_13.cpp 測試 stringstream
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
struct PersonInfo {
string name;
vector<string> phones;
};
string format(const string &s) {
return s;
}
bool valid(const string &s) {
return true;
}
int main(int argc, char *argv[])
{
string line, word; // 分別保存來自輸入的一行和單詞
vector<PersonInfo> people; // 分別保存來自輸入的所有記錄
istringstream record;
if (argc != 2) {
cerr << "請給出文件名" << endl;
return -1;
}
ifstream in(argv[1]);
if (!in) {
cerr << "無法打開輸入文件" << endl;
return -1;
}
while (getline(in, line)) {
PersonInfo info; // 創建一個保存此記錄
record.clear(); // 重複使用字符串流時,每次都要調用clear
record.str(line); // 將記錄綁定到剛讀入的行
record >> info.name; // 讀取名字
while (record >> word) { // 讀取電話號碼
info.phones.push_back(word); // 保存它們
}
people.push_back(info); // 將此記錄追加到people末尾
}
ostringstream os;
for (const auto &entry : people) { // 對people中每一項
ostringstream formatted, badNums; // 每個循環步創建的對象
for (const auto &nums : entry.phones) { // 對每個數
if (!valid(nums)) {
badNums << " " << nums; // 將數的字符串形式存入badNums
} else {
// 將格式化的字符串“寫入”formatted
formatted << " " << format(nums);
}
}
if (badNums.str().empty()) { // 沒有錯誤的數
// 打印名字和格式化的數
os << entry.name << " " << formatted.str() << endl;
} else {
// 打印名字和錯誤的數
cerr << "input error: " << entry.name
<< " invalid number(s) " << badNums.str() << endl;
}
}
cout << os.str() << endl;
return 0;
}