1. 程式人生 > >C++的IO流

C++的IO流

1.回憶C語言的IO流

getchar()          //字元輸入函式
putchar()          //字元輸出函式
fgetc()/getc()     //字元輸入函式
fputc()/putc()     //字元輸入函式
fgets()/gets()     //文字行輸出函式
fputs()/puts()     //文字行輸出函式
printf()/scanf()   //標準格式化輸入輸出函式
fprintf()/fscanf() //所有流的輸入輸出函式
fread()/fwrite()   //文字的輸入輸出

注:上述函式用法總結我的另一篇部落格:https://blog.csdn.net/hansionz/article/details/80884217

在C語言中,我們使用最頻繁的函式是printf、scanf。C語言的IO函式是藉助緩衝區實現的。緩衝區的讀寫速度較快,裝置和程式碼都能很快的從其中複製資料和寫入資料,從而完成IO操作。

在這裡插入圖片描述

緩衝區的緩衝方式分為三類:

  • 全緩衝:當緩衝區寫滿之後才刷新出來
  • 行緩衝:緩衝區遇到換行符既重新整理
  • 無緩衝:寫入的資料立即被刷新出來

輸入/輸出緩衝區存在的原因:

  • 可以遮蔽掉低階I/O的實現,低階I/O的實現依賴作業系統本身核心的實現,所以如果能夠遮蔽這部分的差異,可以很容易寫出可移植的程式
  • 可以使用這部分的內容實現“行”讀取的行為,對於計算機
    而言是沒有“行”這個概念。有了這緩衝區,就可以定義“行”的概念,然後按“行”解析緩衝區的內容

2.流的概念

“流”即是流動的意思,是物質從一處另一處流動的過程,是對一種有序連續且具有方向性的資料的抽象描述C++流是指資訊從外設向計算機內部(如記憶體) 輸入或者從記憶體外設輸出的過程。這種輸入輸出的過程被形象的比喻為“流”。流的特點是連續性的,具有方向性C語言的流是基於緩衝區實現的,輸入輸出的區別在於方向不同和對資料的操作不同。

流可以分為兩種:

  • 文字流:在緩衝區中讀出和寫入時是基於ASCIIUnicode字元編碼的。寫入緩衝區的最大長度規定為254個字元。在文字檔案結束時規定以一個回車符
    換行符結尾,在寫入緩衝區時,會將\n轉換為回車換行符,在從緩衝區中讀取出資料時,就會將回車換行符轉換為換行符
  • 二進位制流:二進位制的流在向緩衝區中寫入或讀出檔案時,不進行字元的轉換,直接從裝置或檔案中讀取,在這個過程中不發生任何改變,原先是什麼樣子,讀取之後還是什麼樣子,它是基於二進位制數字。

對於每一個ANSI C程式⽽而言,至少開啟三個流:標準輸入(stdin)、標準輸出(stdout)、標準錯誤(stderr),他們都是一個指向FILE結構的指標。

3.C++中的IO流

為了實現資料的流動,C++定義了I/O標準類庫,這些每個類都稱為流/流類,用以完成某方面的功能。C++的IO操作編譯時會對型別嚴格的檢查,所以C++的IO是類型安全的,而C語言中的輸入輸出並不是型別安全的

C++中三種IO操作:標準IO 檔案IO 串IO。

  • 標準IO:相容了C中的標準的輸入輸出,以鍵盤和螢幕為操作物件,從鍵盤輸入或從螢幕輸出
  • 檔案IO:以磁碟檔案為輸入輸出的物件,資料在磁碟中寫入和讀出
  • 串IO:對記憶體中指定的空間進行輸入輸出。通常指定一個字元陣列作為儲存空間(實際上可以利用該空間儲存任何資訊)。這種輸入和輸出稱為字串輸入輸出

C++系統實現了一個龐大的類庫,其中ios為基類,其他類都是直接或間接派生自ios類。這些類可以對基本型別進行輸入輸出,也可以對自定義的型別進行輸入輸出,這些類體現了C++標準IO庫的可擴充套件性
在這裡插入圖片描述

C++在類庫中定義了四個全域性流的物件:

  • cin : 標準輸入流,對應鍵盤
  • cout:標準輸出流,對應顯示器
  • clog:標準輸出錯誤流,對應顯示器
  • cerr:標準輸出錯誤流,對應顯示器

注意:

  • 使用cin輸入的時候,鍵盤輸入的資料儲存在緩衝區中,當要提取時,是從緩衝區中拿。如果一次輸入過多,會留在那兒慢慢用。如果輸入錯了,必須在回車之前修改,如果回車鍵按下就無法修改。只有把輸入緩衝區中的資料取完後,才要求輸入新的資料。
  • 輸入的資料型別必須與要提取的資料型別一致,否則會報錯。出錯只是在流的狀態字state中對應位置1,程式繼續執行。
  • 空格和回車都可以作為資料之間的分格符,所以多個數據可以在一行輸入,也可以分行輸入。但如果是字元型和字串,則空格(ASCII碼為32)無法用cin輸入,字串中也不能有空格回車符也無法讀入,因為遇到回車說明輸入結束。

4.檔案流物件

C++中根據檔案內容的資料格式分為二進位制檔案文字檔案,在類庫中實現了對檔案操作的類。

fstream庫中有三個類流:

  • ifstream :支援從磁碟檔案輸入(istream)
  • ofstream 支援向磁碟檔案輸出(ostream)
  • fstream 支援從磁碟檔案輸入和輸出

在使用時,我們需要建立一個流物件來進行操作,cin cout等流物件是在iostream中建立好的(在上邊的類圖中可以看到)。

ifstream ifile //輸入使用
ofstream ofile //輸出使用
fstream iofile //輸入輸出使用

C++對於檔案操作和C語言一樣,在類中有關於開啟,關閉,寫入,和讀出的對檔案操作的成員函式。

  • 開啟檔案:將一個流物件與一個檔案相關聯
//通過建構函式
std::ofstream ofile("test1.txt",std::ofstream::out);
引數:檔名和請求標誌位
//使用成員函式open
std::ofstream ofile;
ofile.open("test1.txt",std::ofstream::out)

如果該檔案已經開啟,open會呼叫失敗。

請求標誌位如下:

標誌 功能
in(input) 開啟檔案讀,內部流緩衝區支援輸入操作
out(output) 檔案開啟供寫入,內部流緩衝區支援輸出操作
binary 執行二進位制操作
app()append 追加,所有的操作都發生在檔案的末尾
ate(at end) 輸出位置在檔案的末尾
trunc(trucate) 開啟檔案之前存於檔案之中的內容都會被丟棄

注:檔案開啟預設位文字檔案許可權標誌位之間可以使用|進行組合。ofstream的流物件預設是outifstream預設是in 所以可以不用寫。

  • close:斷開當前檔案流物件之間的關聯
ofile.colse();

如果當前流物件沒有開啟檔案,則呼叫時就會失敗。

  • 在對文字檔案進行操作時可以用插入 >> 運算子提取 << 運算子進行
ofstream ofile("test1.txt",std::ofstream::out);
ofile << "sdasdas";  //表示向檔案中輸入一個數,輸出到流上

char a[1024];
ifstream ifile("test1.txt",std::ifstream::in);
ifile >> a;  //讀出檔案的資料,輸入到流中
  • write:ofstream類中提供了write成員函式,用於向檔案中寫入資料
ostream& write (const char* s, streamsize n);
  • read:ifstream類中提供了read成員,用於從檔案中讀取資料
istream& read (char* s, streamsize n);

二進位制讀寫時,我們採用writeread成員函式進行操作。

  • 其他的成員函式入put() \ get()
istream& get (char& c); //支援從流中讀取單個字元
ostream& put (char c);  //將單個字元插入到流中

使用檔案IO流用文字及二進位制方式讀寫配置檔案:

#include <iostream>
#include <fstream>
#include <string>

using namespace std;
class ConfigInfo
{
public:
  int port;
  char ip[32];
};
class ConfigManager 
{
public:
  ConfigManager(const char* file = "test.config")
    :configfile(file)
    {}
  //二進位制讀寫
  void WriteBinary(const ConfigInfo& info)
  {
    ofstream ofile(configfile, ofstream::binary | ofstream::out);
    ofile.write((const char*)&info, sizeof(info));
    ofile.close();
  }
  void ReadBinary(ConfigInfo& info)
  {
    ifstream ifile(configfile, ifstream::binary | ifstream::in);
    ifile.read((char*)&info, sizeof(info));
    ifile.close();
  }
  //文字讀寫
  void WriteText(const ConfigInfo& info)
  {
    ofstream ofile(configfile);
    ofile << info.port << endl; 
    ofile << info.ip << endl;
    ofile.close();
  }
  void ReadText(ConfigInfo& info)
  {
    ifstream ifile(configfile);
    ifile >> info.port;
    ifile >> info.ip;
    ifile.close();
  }
private:
  string configfile;
};
int main()
{
  ConfigManager man;
  ConfigInfo rinfo;
  ConfigInfo winfo;

  winfo.port = 80;
  strcpy(winfo.ip,"127.0.0.1");

  //二進位制讀寫
  man.WriteBinary(winfo);
  man.ReadBinary(rinfo);
  cout << rinfo.port << endl;
  cout << rinfo.ip << endl;

  //文字讀寫
  man.WriteText(winfo);
  man.ReadText(rinfo);
  cout << rinfo.ip << endl;
  cout << rinfo.port <<endl;

  return 0;
}