1. 程式人生 > >C++ IO流的概念及流類庫、檔案操作

C++ IO流的概念及流類庫、檔案操作

IO流的概念及流類庫

當程式與外界進行資訊交換時,存在兩個物件,一個是程式中的物件,另一個是檔案物件。
流是資訊流動的一種抽象,它負責在資料的生產者和資料的消費者之間建立聯絡,並管理資料的流動

流物件與檔案操作

  • 程式建立一個流物件
  • 指定這個流物件與某個檔案物件建立連線
  • 程式操作流物件
  • 流物件通過檔案系統對所連線的檔案物件產生作用

提取與插入

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

這裡寫圖片描述

輸出流概述

C++中有三個重要的輸出流

  • 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函式
    • 把一個字元寫到輸出流中
  • writer函式
    • 將記憶體中的一塊內容寫到一個檔案輸出流中
  • seekptellp函式
    • 操作檔案流的內部指標
  • close函式
    • 關閉與個檔案輸出流關聯的磁碟檔案
  • 錯誤處理函式
    • 在寫到一個流時進行錯誤處理

向文字檔案輸出

插入(<<)運算子

  • 為所有標準c++資料型別預先設計的,用於傳送位元組到一個輸出流物件

操縱符(manipulator

  • 插入運算子與操縱符一起工作
    • 輸出格式控制
  • 很多操縱符都定義在
    • ios_base類中(如hex())、<iomanip>標頭檔案(如setprecision()
  • 控制輸出寬度
    • 在流中放入setw操縱符或呼叫width成員函式為每個項指定輸出寬度
  • setwwidth僅影響緊隨其後的輸出項,但其它流格式操縱符是持久的,保持有效直到發生改變
  • decocthex操縱符設定輸入和輸出的預設進位制

例子:使用wisth控制輸出寬度

#include <iostream>
using namespace std;

int main(void)
{
    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
請按任意鍵繼續. . .

同樣的例子用操縱符實現:注意包含相應標頭檔案

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main(void)
{
    double values[] = { 1.23,35.36,653.7,4358.24 };
    string names[] = { "Zoot","Jimmy","AI","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
    AI     653.7
  Stan   4358.24
請按任意鍵繼續. . .

控制對齊方式

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main(void)
{
    double values[] = { 1.23,35.36,653.7,4358.24 };
    string names[] = { "Zoot","Jimmy","AI","Stan" };
    for (int i = 0; i < 4; i++) {
        cout << setiosflags(ios_base::left)
            <<setw(6)
            <<names[i] 
            <<resetiosflags(ios_base::left)
            <<setw(10)
            <<values[i]<< endl;
    }
    return 0;
}

程式執行結果:

Zoot        1.23
Jimmy       35.36
AI          653.7
Stan        4358.24
請按任意鍵繼續. . .

setiosflags操縱符

  • 這個程式中,通過使用帶引數的setiosflags操縱符來設定左對齊,setiosflags定義在標頭檔案iomanip
  • 引數ios_base::leftios_base靜態常量,因此引用時必須包括ios_base::字首。
  • 這裡需要用resetiosflags操縱符關閉左對齊標誌。setiosflags不同於widthsetw,它的影響是持久的,直到resetiosflags重新恢復預設為止
  • setiosflags的引數是該流的格式標誌值,可用按位或(|)運算子進行組合
    這裡寫圖片描述

精度

  • 浮點數輸出精度預設值是6,例如:3466.98
  • 要改變精度:setprecision操作符(定義在iomanip中)
  • 如果不指定fixedscientific,精度值表示有效數字位數
  • 如果設定了ios_base::fixedios_base::scientific精度值表示小數點之後的位數

控制輸出精度例子(不設定fixed或scientific)

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main(void)
{
    double values[] = { 1.23,35.36,653.7,4358.24 };
    string names[] = { "Zoot","Jimmy","AI","Stan" };
    for (int i = 0; i < 4; i++) {
        cout << setiosflags(ios_base::left)
            << setw(6)
            << names[i]
            <<resetiosflags(ios_base::right)
            <<setw(10)
            <<setprecision(1)<<values[i]<<endl;

    }
    return 0;
}

程式執行效果:

Zoot  1
Jimmy 4e+01
AI    7e+02
Stan  4e+03
請按任意鍵繼續. . .

控制輸出精度例子(設定fixed,setprecision表示小數點後幾位)

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main(void)
{
    double values[] = { 1.23,35.36,653.7,4358.24 };
    string names[] = { "Zoot","Jimmy","AI","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::right)
            <<setw(10)
            <<setprecision(1)<<values[i]<<endl;  //表示小數點後的位數

    }
    return 0;
}

程式執行結果:

Zoot  1.2
Jimmy 35.4
AI    653.7
Stan  4358.2
請按任意鍵繼續. . .

控制輸出精度例子(設定scientific科學計數法,setprecision表示小數點後幾位)

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main(void)
{
    double values[] = { 1.23,35.36,653.7,4358.24 };
    string names[] = { "Zoot","Jimmy","AI","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::right)
            <<setw(10)
            <<setprecision(1)<<values[i]<<endl;

    }
    return 0;
}
Zoot  1.2e+00
Jimmy 3.5e+01
AI    6.5e+02
Stan  4.4e+03
請按任意鍵繼續. . .

向二進位制檔案輸出

二進位制檔案流

  • 使用ofstream建構函式中的模式參量指定二進位制檔案輸出模式
  • 以通常方式構造一個流,然後使用setmode成員函式,在檔案開啟後改變模式
  • 通過二進位制檔案輸出流物件完成輸出

二進位制檔案輸出例子

#include <iostream>
#include <iomanip>
#include <string>
#include <fstream>
using namespace std;

struct Date {
    int mon, day, year;
};
int main(void)
{
    Date dt = {6,10,92};
    ofstream file("date.dat", ios_base::binary);
    file.write(reinterpret_cast<char *>(&dt),sizeof(dt));
    file.close();
    return 0;
}

在工程目錄下可以檢視到這個二進位制檔案

向字串輸出檔案

典型應用:將資料輸出到字串可以將資料轉化為字串型別

字串輸出流

  • 用於構造輸出流(ostringstream
  • 功能
    • 支援ofstream類的除openclose外的所有操作
    • str函式可以返回當前已構造的字串
  • 典型應用
    • 將數值轉化為字串

將數值轉化為字串例子

#include <iostream>
#include <sstream>
#include <string>
using namespace std;

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

int main(void)
{
    string s1 = toString(5);
    cout << s1 << endl;
    string s2 = toString(1.2);
    cout << s2 << endl;
    return 0;
}

程式碼執行效果:

5
1.2
請按任意鍵繼續. . .

輸入流類

重要的輸入流類

  • istream類最適合用於順序文字模式輸入,cin是它的例項
  • ifstream類支援磁碟檔案輸入
  • istringstream

構造輸入流物件

  • 如果在建構函式中指定一個檔名,在構造該物件時該檔案自動開啟。
    ifstream myFile("filename");
  • 在呼叫預設建構函式之後用open函式開啟檔案。
    ifstream myFile; //建立一個檔案流物件
    myFile.open("filename");//開啟檔案“filename”
  • 開啟檔案模式可以指定
    ifstream myFile("filename",ios_base::in|ios_base::binary);

使用提取運算子從文字檔案輸入

  • 提取運算子<<對於所有標準C++資料型別都是預先設計好的
  • 是從一個輸入流物件獲取位元組最容易的方法
  • ios類中的很多操縱符都可以應用到輸入流。但是隻有少數幾個對輸入流物件具有實際影響,其中最重要的是進位制操縱符decocthex

輸入流相關函式

  • open函式把該流與一個特定磁碟檔案相關聯
  • get函式的功能與提取運算子(>>)很相像,主要的不同點是get函式在讀入資料時包括空白字元。
  • getline的功能是從輸入流中讀取多個字元,並且允許指定輸入終止字元讀取完成後,從讀取的內容中刪除終止字元。
  • read成員函式從一個檔案讀位元組到一個指定的記憶體區域,由長度魯數確定要讀的位元組數。當遇到檔案結束或者在文字模式檔案中遇到檔案結東標記字元時結東束讀取
  • seekg函式用來設定檔案輸入流中讀取資料位置的指標
  • tellg函式返回當前檔案讀指標的位置
  • close函式關閉與一個檔案輸入流關聯的磁碟檔案

get函式應用舉例

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

為輸入流指定一個終止字元

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

程式碼執行結果:

Type a line terminated by 't'
qweteeeee
qwe
請按任意鍵繼續. . .

從檔案讀一個二進位制記錄到一個結構中

#include <fstream>
#include <cstring>
#include <iostream>
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::out | ios_base::binary);
    if (is) {
        SalaryInfo employee2;
        is.read(reinterpret_cast<char *>(&employee2), sizeof(employee2));
        cout << employee2.id << employee2.salary << endl;
    }
    else {
        cout << "ERROR:Can not open file" << endl;
    }
    is.close();
    return 0;
}

程式運算結果:

6000018000
請按任意鍵繼續. . .

函式位置指標

#include <fstream>
#include <cstring>
#include <iostream>
using namespace std;
struct SalaryInfo {
    unsigned id;
    double salary;
};
int main() {
    int values[] = {3,7,0,5,4};
    ofstream os("payroll", ios_base::out | ios_base::binary);
    os.write(reinterpret_cast<char *>(&values),sizeof(values));
    os.close();
    ifstream is("payroll", ios_base::out | ios_base::binary);
    if (is) {
        int v;
        is.seekg(3 * sizeof(int));
        is.read(reinterpret_cast<char *>(&v), sizeof(int));
        cout << "the 4th is"<<v<< endl;
    }
    else {
        cout << "ERROR:Can not open file" << endl;
    }
    is.close();
    return 0;
}

程式碼執行結果:

the 4th is 5
請按任意鍵繼續. . .

字串輸入流

字串輸入流(istringstream

  • 用於從字串讀取資料
  • 在建構函式中設定要讀取的字串
  • 功能
    • 支援ifstream類的除openclose外的所有操作
  • 典型應用
    • 將字串轉化為數值

從字串輸入

#include <fstream>
#include <cstring>
#include <iostream>
#include <sstream>
using namespace std;
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派生的,他們是fstreamstringstream。這些類繼承了前面描述的istreamostream類的功能

fstream

  • fstream類支援磁碟檔案輸入或輸出
  • 如果需要在同一程式中從一個特定磁碟檔案讀並寫到該磁碟檔案,可以構造一個fstream物件
  • 一個fstream物件是有兩個邏輯子流的單個流,兩個子流一個用於輸入,另一個用於輸出

stringstream

stringstream類支援面向字串的輸入輸出可以用與對同一個字串的內容交替讀寫,同樣是由兩個邏輯子流構成