C++的IO類庫

在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;
}

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章