天天看點

流類庫與輸入輸出

目錄

    • I/O流的概念及流類庫結構
      • 程式與外界環境的資訊交換
      • 流對象與檔案操作
      • 提取與插入
      • 流類庫結構
      • 流類清單
    • 輸出流概述
      • 最重要的三個輸出流
      • 預先定義的輸出流對象
      • 标準輸出換向, 這是個啥
      • 構造輸出流對象
      • 檔案輸出流成員函數的三種類型
      • 檔案輸出流成員函數
    • 向文本檔案輸出
      • 插入運算符
      • 操縱符(manipulator)
      • 例11-1 使用`width`控制輸出寬度
      • 例11-2使用`setw`操縱符指定寬度
      • 例11-3設定對齊方式
      • `setiosflags`操縱符
      • setiosflags的參數(流的格式辨別)
      • 精度
      • 例11-4控制輸出精度——未指定fixed或scientific
      • 例11-4控制輸出精度——指定fixed
      • 例11-4控制輸出精度——指定scientific
    • 向二進制檔案輸出
      • 二進制檔案流
      • 例11-5 向二進制檔案輸出
    • 向字元串輸出
      • 字元串輸出流( ostringstream )
      • 例11-6用ostringstream将 數值 轉換為 字元串
    • 輸入流概述
      • 重要的輸入流類
      • 構造輸入流對象
      • 使用提取運算符從文本檔案輸入
      • 輸入流相關函數
    • 輸入流應用舉例
      • 例11-7 get函數應用舉例
      • 例11-8為輸入流指定一個終止字元:
      • 例11-9 從檔案讀一個二進制記錄到一個結構中
      • 例11-10用seekg函數設定位置指針
      • 例11-11 讀一個檔案并顯示出其中 0 元素的位置
    • 從字元串輸入
      • 字元串輸入流( istringstream)
      • 例11-12用istringstream将字元串轉換為數值
    • 輸入/輸出流
      • 兩個重要的輸入/輸出流
      • fstream類
      • stringstream類
      • 達到的目标

I/O流的概念及流類庫結構

程式與外界環境的資訊交換

  • 當程式與外界環境進行資訊交換時,存在着兩個對象:程式中的對象、檔案對象。
  • 流是一種抽象,負責在資料的生産者和資料的消費者之間建立聯系,并管理資料的流動。

流對象與檔案操作

  • 程式建立一個流對象
  • 指定這個流對象與某個檔案對象建立連接配接
  • 程式操作流對象
  • 流對象通過檔案系統對所連接配接的檔案對象産生作用。

提取與插入

  • 讀操作在流資料抽象中被稱為(從流中)提取
  • 寫操作被稱為(向流中)插入。

流類庫結構

流類庫與輸入輸出

流類清單

流類庫與輸入輸出

輸出流概述

最重要的三個輸出流

  • ostream
  • ofstream
  • ostringstream

預先定義的輸出流對象

  • cout 标準輸出
  • cerr 标準錯誤輸出,沒有緩沖,發送給它的内容立即被輸出。
  • clog 類似于cerr,但是有緩沖,緩沖區滿時被輸出。

标準輸出換向, 這是個啥

ofstream fout("b.out");
streambuf*  pOld  =cout.rdbuf(fout.rdbuf());  
//…
cout.rdbuf(pOld);
           

構造輸出流對象

  • ofstream類支援磁盤檔案輸出
  • 如果在構造函數中指定一個檔案名,當構造這個檔案時該檔案是自動打開的
    ofstream myFile("filename");
               
  • 可以在調用預設構造函數之後使用open成員函數打開檔案
    ofstream myFile; //聲明一個檔案輸出流對象
    myFile.open("filename");   //打開檔案,使流對象與檔案建立聯系
               
  • 在構造對象或用open打開檔案時可以指定模式
    ofstream myFile("filename", ios_base::out | ios_base::binary);
               

檔案輸出流成員函數的三種類型

  • 與操縱符等價的成員函數。
  • 執行非格式化寫操作的成員函數。
  • 其它修改流狀态且不同于操縱符或插入運算符的成員函數。

檔案輸出流成員函數

  • open

    函數

把流與一個特定的磁盤檔案關聯起來。

需要指定打開模式。

  • put

    函數

把一個字元寫到輸出流中。

  • write

    函數

把記憶體中的一塊内容寫到一個檔案輸出流中

  • seekp

    tellp

    函數

操作檔案流的内部指針

  • close

    函數

關閉與一個檔案輸出流關聯的磁盤檔案

  • 錯誤處理函數

在寫到一個流時進行錯誤處理

向文本檔案輸出

标準輸出裝置顯示器被系統看作文本檔案,是以我們以向标準裝置輸出為例,介紹文本檔案輸出格式控制

插入運算符

  • 插入(<<)運算符

    • 為所有标準C++資料類型預先設計的,用于傳送位元組到一個輸出流對象。

操縱符(manipulator)

  • 插入運算符與操縱符一起工作
    • 控制輸出格式。
  • 很多操縱符都定義在
    • ios_base類中(如hex())、頭檔案(如setprecision())。
  • 控制輸出寬度
    • 在流中放入setw操縱符或調用width成員函數為每個項指定輸出寬度。
  • setw

    width

    僅影響緊随其後的輸出項,但其它流格式操縱符保持有效直到發生改變。
  • dec

    oct

    hex

    操縱符設定輸入和輸出的預設進制。

例11-1 使用

width

控制輸出寬度

#include <iostream>
using namespace std;

int main() {
    double values[] = { 1.23, 35.36, 653.7, 4358.24 };
    for(int i = 0; i < 4; i++) {
        cout.width(10);          // 設定寬度
        cout << values[i] << endl;
    }
    return 0;
}
輸出結果:
      1.23
     35.36
     653.7
   4358.24
           

例11-2使用

setw

操縱符指定寬度

//11_2.cpp
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main() {
    double values[] = { 1.23, 35.36, 653.7, 4358.24 };
    string names[] = { "Zoot", "Jimmy", "Al", "Stan" };
    for (int i = 0; i < 4; i++)
      cout << setw(6) << names[i]         // 設定寬度
           << setw(10) << values[i] << endl;
    return 0;
}
輸出結果:
  Zoot      1.23
 Jimmy     35.36
    Al     653.7
  Stan   4358.24
           

例11-3設定對齊方式

//11_3.cpp
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main() {
    double values[] = { 1.23, 35.36, 653.7, 4358.24 };
    string names[] = { "Zoot", "Jimmy", "Al", "Stan" };
    for (int i=0;i<4;i++)
      cout << setiosflags(ios_base::left)          //左對齊
           << setw(6) << names[i]
           << resetiosflags(ios_base::left)    // 使用resetiosflags複位之前的左對齊設定
           << setw(10) << values[i] << endl;
    return 0;
}
輸出結果:
Zoot        1.23
Jimmy      35.36
Al         653.7
Stan     4358.24
           

setiosflags

操縱符

  • 這個程式中,通過使用帶參數的setiosflags操縱符來設定左對齊,setiosflags定義在頭檔案

    iomanip

    中。
  • 參數ios_base::left是ios_base的靜态常量,是以引用時必須包括ios_base::字首。
  • 這裡需要用

    resetiosflags

    操縱符關閉左對齊标志。setiosflags不同于width和setw,它的影響是持久的,直到用

    resetiosflags

    重新恢複預設值時為止 。
  • setiosflags的參數是該流的格式标志值,可用按位或(|)運算符進行組合

setiosflags的參數(流的格式辨別)

  • ios_base::skipws 在輸入中跳過空白 。
  • ios_base::left 左對齊值,用填充字元填充右邊。
  • ios_base::right 右對齊值,用填充字元填充左邊(預設對齊方式)。
  • ios_base::internal 在規定的寬度内,指定字首符号之後,數值之前,插入指定的填充字元。
  • ios_base::dec 以十進制形式格式化數值(預設進制)。
  • ios_base::oct 以八進制形式格式化數值 。
  • ios_base::hex 以十六進制形式格式化數值。
  • ios_base::showbase 插入字首符号以表明整數的數制。
  • ios_base::showpoint 對浮點數值顯示小數點和尾部的0 。
  • ios_base::uppercase 對于十六進制數值顯示大寫字母A到F,對于科學格式顯示大寫字母E 。
  • ios_base::showpos 對于非負數顯示正号(“+”)。
  • ios_base::scientific 以科學格式顯示浮點數值。
  • ios_base::fixed 以定點格式顯示浮點數值(沒有指數部分) 。
  • ios_base::unitbuf 在每次插入之後轉儲并清除緩沖區内容。

精度

  • 浮點數輸出精度的預設值是6,例如:3466.98。
  • 要改變精度:

    setprecision

    操縱符(定義在頭檔案

    iomanip

    中)。
  • 如果不指定

    fixed

    scientific

    ,精度值表示有效數字位數。
  • 如果設定了ios_base::fixed或 ios_base::scientific,精度值表示小數點之後的位數。

例11-4控制輸出精度——未指定fixed或scientific

//11_4_1.cpp
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main() {
    double values[] = { 1.23, 35.36, 653.7, 4358.24 };
    string names[] = { "Zoot", "Jimmy", "Al", "Stan" };
    for (int i=0;i<4;i++)
      cout << setiosflags(ios_base::left)
        << setw(6) << names[i]
        << resetiosflags(ios_base::left)//清除左對齊設定
        << setw(10) << setprecision(1) << values[i] << endl;
    return 0;
}
輸出結果:
Zoot           1
Jimmy     4e+001
Al        7e+002
Stan      4e+003
           

例11-4控制輸出精度——指定fixed

//11_4_2.cpp
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
int main() {
    double values[] = { 1.23, 35.36, 653.7, 4358.24 };
    string names[] = { "Zoot", "Jimmy", "Al", "Stan" };
    cout << setiosflags(ios_base::fixed);   
    for (int i=0;i<4;i++)
      cout << setiosflags(ios_base::left)
           << setw(6) << names[i]
           << resetiosflags(ios_base::left)//清除左對齊設定
           << setw(10) << setprecision(1) << values[i] << endl;
    return 0;
}
輸出結果:
Zoot         1.2
Jimmy       35.4
Al         653.7
Stan      4358.2
           

例11-4控制輸出精度——指定scientific

//11_4_3.cpp
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
int main() {
    double values[] = { 1.23, 35.36, 653.7, 4358.24 };
    string names[] = { "Zoot", "Jimmy", "Al", "Stan" };
  cout << setiosflags(ios_base::scientific);
    for (int i=0;i<4;i++)
      cout << setiosflags(ios_base::left)
        << setw(6) << names[i]
        << resetiosflags(ios_base::left)//清除左對齊設定
        << setw(10) << setprecision(1) << values[i] << endl;
    return 0;
}
輸出結果:
Zoot    1.2e+000
Jimmy   3.5e+001
Al      6.5e+002
Stan    4.4e+003
           

向二進制檔案輸出

二進制檔案流

  • 使用

    ofstream

    構造函數中的模式參量指定二進制輸出模式;
  • 以通常方式構造一個流,然後使用

    setmode

    成員函數,在檔案打開後改變模式;
  • 通過二進制檔案輸出流對象完成輸出。

例11-5 向二進制檔案輸出

//11_5.cpp
#include <fstream>
using namespace std;
struct Date { 
    int mon, day, year;  
};
int main() {
    Date dt = { 6, 10, 92 };
    ofstream file("date.dat", ios_base::binary);
    // 為什麼要轉變成char *, 這是因為要以二進制格式輸出,計算的是位元組數, 而char本身就是位元組類型
    file.write(reinterpret_cast<char *>(&dt),sizeof(dt));  
    file.close();
    return 0;
}
           

向字元串輸出

将字元串作為輸出流的目标,可以實作将其他資料類型轉換為字元串的功能

字元串輸出流( ostringstream )

  • 用于構造字元串
  • 功能
    • 支援ofstream類的除open、close外的所有操作
    • str

      函數可以傳回目前已構造的字元串
  • 典型應用
    • 将數值轉換為字元串

例11-6用ostringstream将 數值 轉換為 字元串

//11_6.cpp
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

//函數模闆toString可以将各種支援 “<<“ 插入符 的類型的對象轉換為字元串。

template <class T>
inline string toString(const T &v) {
    ostringstream os;   //建立字元串輸出流
    os << v;        // 将變量v的值寫入字元串流
    return os.str();    // 傳回輸出流生成的字元串
}

int main() {
    string str1 = toString(5);
    cout << str1 << endl;
    string str2 = toString(1.2);
    cout << str2 << endl;
    return 0;
}

輸出結果:
5
1.2
           

輸入流概述

重要的輸入流類

  • istream類最适合用于順序文本模式輸入。cin是其執行個體。
  • ifstream類支援磁盤檔案輸入。
  • istringstream

構造輸入流對象

  • 如果在構造函數中指定一個檔案名,在構造該對象時該檔案便自動打開。 ifstream myFile(“filename”);
  • 在調用預設構造函數之後使用open函數來打開檔案。 ifstream myFile; //建立一個檔案流對象 myFile.open(“filename”); //打開檔案"filename”
  • 打開檔案時可以指定模式 ifstream myFile(“filename”, iosbase::in | iosbase::binary);

使用提取運算符從文本檔案輸入

  • 提取運算符(>>)對于所有标準C++資料類型都是預先設計好的。
  • 是從一個輸入流對象擷取位元組最容易的方法。
  • ios類中的很多操縱符都可以應用于輸入流。但是隻有少數幾個對輸入流對象具有實際影響,其中最重要的是進制操縱符dec、oct和hex。

輸入流相關函數

  • open

    把該流與一個特定磁盤檔案相關聯。
  • get

    功能與提取運算符(>>)很相像,主要的不同點是get函數在讀入資料時包括空白字元。
  • getline

    功能是從輸入流中讀取多個字元,并且允許指定輸入終止字元,讀取完成後,從讀取的内容中删除終止字元。
  • read 從一個檔案讀位元組到一個指定的記憶體區域,由長度參數确定要讀的位元組數。當遇到檔案結束或者在文本模式檔案中遇到檔案結束标記字元時結束讀取。
  • seekg 用來設定檔案輸入流中讀取資料位置的指針。
  • tellg 傳回目前檔案讀指針的位置。
  • close 關閉與一個檔案輸入流關聯的磁盤檔案。

輸入流應用舉例大綱進度:未閱讀

輸入流應用舉例

例11-7 get函數應用舉例

//11_7.cpp
#include <iostream>
using namespace std;
int main() {
    char ch;
    while ((ch = cin.get()) != EOF)  // cin.get()
        cout.put(ch);
    return 0;
}
           

例11-8為輸入流指定一個終止字元:

//11_8.cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
    string line;
    cout << "Type a line terminated by 't' " << endl; 
    getline(cin, line, 't');   // 可以指定終止符
    cout << line << endl; 
    return 0;
}
           

例11-9 從檔案讀一個二進制記錄到一個結構中

//11_9.cpp
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;

struct SalaryInfo {
    unsigned id;
    double salary;
}; 
int main() {
    SalaryInfo employee1 = { 600001, 8000 };
    ofstream os("payroll", ios_base::out | ios_base::binary);
    os.write(reinterpret_cast<char *>(&employee1), sizeof(employee1));
    os.close();
    
    ifstream is("payroll", ios_base::in | ios_base::binary);
    // 為什麼可以進行if的條件判斷?是因為C++會自動調用類型轉換函數 bool()
    if (is) {
        SalaryInfo employee2;
        is.read(reinterpret_cast<char *>(&employee2), sizeof(employee2));
        cout << employee2.id << " " << employee2.salary << endl;
    } else {
        cout << "ERROR: Cannot open file 'payroll'." << endl;
    }
    is.close();
    return 0;
}
           

例11-10用seekg函數設定位置指針

//11_10.cpp, 頭部分省略
int main() {
    int values[] = { 3, 7, 0, 5, 4 };
    ofstream os("integers", ios_base::out | ios_base::binary);
    os.write(reinterpret_cast<char *>(values), sizeof(values));
    os.close();

    ifstream is("integers", ios_base::in | ios_base::binary);
    if (is) {
        is.seekg(3 * sizeof(int));
        int v;
        is.read(reinterpret_cast<char *>(&v), sizeof(int));
        cout << "The 4th integer in the file 'integers' is " << v << endl;
    } else {
        cout << "ERROR: Cannot open file 'integers'." << endl;
    }
    return 0;
}
           

例11-11 讀一個檔案并顯示出其中 0 元素的位置

//11_11.cpp, 頭部分省略
int main() {
    ifstream file("integers", ios_base::in | ios_base::binary);
    if (file) {
        while (file) {//讀到檔案尾file為0
            streampos here = file.tellg();
            int v;
            file.read(reinterpret_cast<char *>(&v), sizeof(int));
            if (file && v == 0) 
            cout << "Position " << here << " is 0" << endl;
        }
    } else {
        cout << "ERROR: Cannot open file 'integers'." << endl;
    }
    file.close();
    return 0;
}
           

從字元串輸入

将字元串作為文本輸入流的源,可以将字元串轉換為其他資料類型

字元串輸入流( istringstream)

  • 用于從字元串讀取資料
  • 在構造函數中設定要讀取的字元串
  • 功能
    • 支援ifstream類的除open、close外的所有操作
  • 典型應用
    • 将字元串轉換為數值

例11-12用istringstream将字元串轉換為數值

//11_12.cpp, 頭部分省略
template <class T>
inline T fromString(const string &str) {
    istringstream is(str);  //建立 字元串 輸入流, 傳入一個字元串
    T v;
    is >> v;    //從字元串輸入流中讀取變量v
    return v;   //傳回變量v
}

int main() {
    int v1 = fromString<int>("5");
    cout << v1 << endl;
    double v2 = fromString<double>("1.2");
    cout << v2 << endl;
    return 0;
}
輸出結果:
5
1.2
           

輸入/輸出流

兩個重要的輸入/輸出流

  • 一個iostream對象可以是資料的源或目的。
  • 兩個重要的I/O流類都是從iostream派生的,它們是

    fstream

    stringstream

    。這些類繼承了前面描述的istream和ostream類的功能。

fstream類

  • fstream類支援磁盤檔案輸入和輸出。
  • 如果需要在同一個程式中從一個特定磁盤檔案讀并寫到該磁盤檔案,可以構造一個fstream對象。
  • 一個fstream對象是有兩個邏輯子流的單個流,兩個子流一個用于輸入,另一個用于輸出。

stringstream類

  • stringstream類支援面向

    字元串

    的輸入和輸出
  • 可以用于對同一個字元串的内容交替讀寫,同樣是由兩個邏輯子流構成。

達到的目标

  • 能夠将資料持久化。
  • 能夠處理文本檔案和二進制檔案。
  • 能夠利用字元串流進行字元串與其他類型之間的轉換