流緩衝區迭代器(輸入流緩衝區迭代器和輸出流緩衝區迭代器)詳解

2020-07-16 10:04:31
流緩衝區迭代器不同於流疊代器,流疊代器只會傳送字元到流緩衝區或從流緩衝區讀出字元。它們可以直接存取流的緩衝區,因此不包含插入和提取運算子。也沒有資料的轉換,資料之間也不需要分隔符,即使有分隔符,也可以自己處理它們。因為它們讀寫資料時沒有資料轉換,流緩衝區迭代器適用於二進位制檔案。流緩衝區迭代器讀寫字元的速度比流疊代器快。

istreambuf_iterator 模板定義了輸入疊代器,ostreambuf_iterator 模板定義了輸出疊代器。可以構造讀寫任意 char、 wchar_t、 char 16_t、char32 型別的字元的流緩衝區迭代器。

輸入流緩衝區迭代器

為了生成可以從流中讀取任意給定型別的字元輸入流疊代器,需要將一個流物件傳給建構函式:
std::istreambuf_iterator<char> in {std::cin};
這個物件是一個可以從標準輸入流讀取任意char型別字元的輸入流緩衝區迭代器。預設建構函式生成的物件表示的是流結束疊代器:
std::istreambuf_iterator<char> end_in;
可以用這兩個疊代器從 cin 中讀取字元序列到字串中,直到在單行中輸入的 Ctrl+Z 被作為流結束的信號。例如:
std::cout << "Enter something: ";
string rubbish {in, end_in};
std::cout << rubbish << std::endl; // Whatever you enter will be output
用從鍵盤輸入的全部字元來初始化 string 物件 rubbish,直到遇到流的結束符。輸入流緩衝區迭代器有下面這些成員函數:
  • operator*():返回的是流中當前字元的副本。流的位置不會被提前,因此可以反復地獲取當前字元。
  • operator->():可以存取當前字元的成員,如果它有成員的話。
  • operator++() 和 operator++(int):都會將流的位置移到下一個字元。operator++() 在移動位置後才返回流疊代器,operatar++(int) 在被移動位置之前返回流疊代器的一個代理。字首 ++ 運算子很少使用。
  • equal() 接受另一個輸入流緩衝區迭代器為引數,並返回 true,前提是當前疊代器和引數都不是流結束疊代器,或者都是流結束疊代器。如果它們之中只有一個流結束疊代器,會返回 false。

也有非成員函數,operator==() 和 opemrator!=(),可以用來比較兩個疊代器。我們不得不依靠流的結束來終止輸入。可以用遞增和解除參照運算子來從流中讀取字元,直到找到特定的字元。例如:
std::istreambuf_iterator<char> in {std::cin};
std::istreambuf_iterator<char> end_in;
char end_ch { '*' };
string rubbish;
while(in != end_in && *in != end_ch) rubbish += *in++;
std::cout << rubbish << std::endl; // Whatever you entered up to ' * ' or EOF
while 迴圈會從 cin 中讀取字元,直到識別到流的結束符或者在迴車後輸入一個星號。迴圈體中會運用解除參照運算子來返回流中的當前字元,然後,字尾自增運算子會移動迭代器,使它指向下一個字元。注意迴圈表示式中的解除參照說明它沒有改變迭代器;只要不是 '*',在疊代器遞增之前,迴圈體中的相同字元會被再次讀取。

輸出流緩衝區迭代器

通過傳給建構函式一個流物件,可以生成一個 ostreambuf_iterator 物件來將給定型別的字元寫到流中:
string file_name {"G:/Beginning_STL/junk.txt"};
std::ofstream junk_out {file_name};
std::ostreambuf_iterator<char> out {junk_out};
輸出物件可以將 char 型別的字元寫到檔案輸出流 junk_out 中,junk_out 封裝了名為 junk.txt 的檔案。為了讀取不同型別的字元,例如 char32_t,需要指定模板型別引數作為字元型別。當然,必須為字元型別生成流,因此不能用 ofstream,因為 ofstream 是 basic_ofstream<char> 型別的別名。下面是一個範例:
string file_name { "G:/Beginning_STL/words.txt"}; std::basic_ofstream<char32_t> words_out {file_name};
std::ostreambuf_iterator<char32_t> out {words_out};
這個流緩衝區迭代器可以將 Unicode 字元寫到流緩衝區。有 wchar_t 型別的字元的檔案流被定義為別名 wofstream。

也可以通過將一個流緩衝區的地址傳給建構函式來生成輸出流緩衝區物件。可以這樣來生成上面的 out 物件:
std::ostreambuf_iterator<char> out {junk_out.rdbuf()};
ofstream 物件的成員函數 rdbuf() 會返回流內部的緩衝區的地址。成員函數 rdbuf() 繼承自 ios_base,它是所有流物件的基礎類別。

ostreambuf_iterator 物件有下面這些成員函數:
  • operator=():會將引數字元寫到流緩衝區中。如果識別到 EOF,就說明流緩衝區是滿的,這個寫操作失敗。
  • 當上一次寫緩衝區失敗時,failed() 返回 true。當識別到 EOF 時,會發生這種情況,因為輸出流緩衝區是滿的。
  • operator*():不做任何事。之所以定義它,是因為它需要的 ostreambuf_iterator 物件是一個輸出疊代器。
  • operator++() 和 operator++(int):不做任何事。之所以定義它們,是因為它們需要的 ostreambuf_iterator 物件是一個輸出疊代器。

通常只關心成員函數賦值運算子。下面是使用它的一種方式:
string ad {"Now is the discount of our winter tents! n"};
std::ostreambuf_iterator<char> iter {std::cout}; // Iterator for output to cout
for(auto ch: ad)
    iter = ch;  // Write the character to the stream
執行這段程式碼會將字串逐位元組寫到標準輸出流中。當然,通過呼叫 copy() 演算法也可以得到同樣的結果:
std::copy(std::begin(ad), std::end(ad), std::ostreambuf_iterator<char>{std::cout});
這兩個範例中的編碼方式有些滑稽可笑,因為它們等同於下面這行程式碼:
std::cout << ad;
儘管它並沒有告訴我們關於輸出流緩衝區迭代器的更多資訊。