C++ STL流緩衝區迭代器(streambuf_iterator)

2020-07-16 10:05:22
《C++ STL流疊代器》一節中,講解了輸入流疊代器和輸出疊代器的功能和用法,在此基礎上,本節繼續講解輸入流緩衝區迭代器。

在學習本節之前,讀者有必要先了解什麼是緩衝區,可閱讀《進入緩衝區(快取)的世界》一節做詳細了解。

我們知道在 C++ STL 標準庫中,流疊代器又細分為輸入流疊代器和輸出流疊代器,流緩衝區迭代器也是如此,其又被細分為輸入流緩衝區迭代器和輸出流緩衝區迭代器:
  • 輸入流緩衝區迭代器(istreambuf_iterator):從輸入流緩衝區中讀取字元元素;
  • 輸出流緩衝區迭代器(ostreambuf_iterator):將連續的字元元素寫入到輸出緩衝區中。

流緩衝區迭代器和流疊代器最大的區別在於,前者僅僅會將元素以字元的形式(包括 char、wchar_t、char16_t 及 char32_t 等)讀或者寫到流緩衝區中,由於不會涉及資料型別的轉換,讀寫資料的速度比後者要快。

接下來,將一一對它們的功能和用法做講解。

C++ STL輸入流緩衝區迭代器(istreambuf_iterator)

istreambuf_iterator 輸入流緩衝區迭代器的功能是從指定的流緩衝區中讀取字元元素。

值得一提的是,該型別疊代器本質是一個輸入疊代器,即假設 p 是一個輸入流疊代器,則其只能進行 ++p、p++、*p 操作,同時疊代器之間也只能使用 == 和 != 運算子。

另外,實現該型別疊代器的模板類也定義在<iterator>標頭檔案,並位於 std 名稱空間中。因此,在建立並使用該型別疊代器之前,程式中應包含如下程式碼:
#include <iterator>
using namespace std;

第二行程式碼不是必需的,但如果不用,則程式中在建立該型別的疊代器時,必須手動注明 std 名稱空間(強烈建議初學者使用)。

建立輸入流緩衝區迭代器的常用方式,有以下 2 種:
1) 通過呼叫 istreambuf_iterator 模板類中的預設建構函式,可以建立一個表示結尾的輸入流緩衝區迭代器。要知道,當我們從流緩衝區中不斷讀取資料時,總有讀取完成的那一刻,這一刻就可以用此方式構建的流緩衝區迭代器表示。

舉個例子:
std::istreambuf_iterator<char> end_in;
其中,<> 尖括號中用於指定從流緩衝區中讀取的字元型別。

2) 當然,我們還可以指定要讀取的流緩衝區,比如:
std::istreambuf_iterator<char> in{ std::cin };
除此之外,還可以傳入流緩衝區的地址,比如:
std::istreambuf_iterator<char> in {std::cin.rdbuf()};
其中,rdbuf() 函數的功能是獲取指定流緩衝區的地址。

無論是傳入流緩衝區,還是傳入其地址,它們最終構造的輸入流緩衝區迭代器是一樣的。


下面程式演示了輸入流緩衝區迭代器的用法:
#include <iostream>     // std::cin, std::cout
#include <iterator>     // std::istreambuf_iterator
#include <string>       // std::string
using namespace std;
int main() {
    //建立結束流緩衝區迭代器
    istreambuf_iterator<char> eos;
    //建立一個從輸入緩衝區讀取字元元素的疊代器
    istreambuf_iterator<char> iit(cin);
    string mystring;

    cout << "向緩衝區輸入元素:n";
    //不斷從緩衝區讀取資料,直到讀取到 EOF 流結束符
    while (iit != eos) {
        mystring += *iit++;
    }
    cout << "string:" << mystring;
    return 0;
}
程式執行結果為:

向緩衝區輸入元素:
abc ↙
^Z ↙
string:
abc

注意,只有讀取到 EOF 流結束符時,程式中的 iit 才會和 eos 相等。在 Windows 平台上,使用 Ctrl+Z 組合鍵輸入 ^Z 表示 EOF 流結束符,此結束符需要單獨輸入,或者輸入換行符之後再輸入才有效。

C++ STL輸出流緩衝區迭代器(ostreambuf_iterator)

和 istreambuf_iterator 輸入流緩衝區迭代器恰恰相反,ostreambuf_iterator 輸出流緩衝區迭代器用於將字元元素寫入到指定的流緩衝區中。

實際上,該型別疊代器本質上是一個輸出疊代器,這意味著假設 p 為一個輸出疊代器,則它僅能執行 ++p、p++、*p=t 以及 *p++=t 操作。

另外,和 ostream_iterator 輸出流疊代器一樣,istreambuf_iterator 疊代器底層也是通過過載賦值(=)運算子實現的。換句話說,即通過賦值運算子,每個賦值給輸出流緩衝區迭代器的字元元素,都會被寫入到指定的流緩衝區中。

需要指出的是,istreambuf_iterator 類別範本也定義在<iterator>標頭檔案,並位於 std 名稱空間中,因此使用該型別疊代器,程式中需要包含以下程式碼:
#include <iterator>
using namespace std;

在此基礎上,建立輸出流緩衝區迭代器的常用方式有以下 2 種:
1) 通過傳遞一個流緩衝區物件,即可建立一個輸出流緩衝區迭代器,比如:
std::ostreambuf_iterator<char> out_it (std::cout);
同樣,尖括號 <> 中用於指定要寫入字元的型別,可以是 char、wchar_t、char16_t 以及 char32_t 等。

2) 還可以借助 rdbuf(),傳遞一個流緩衝區的地址,也可以成功建立輸出流緩衝區迭代器:
std::ostreambuf_iterator<char> out_it (std::cout.rdbuf());

下面程式演示了輸出流緩衝區迭代器的用法:
#include <iostream>     // std::cin, std::cout
#include <iterator>     // std::ostreambuf_iterator
#include <string>       // std::string
#include <algorithm>    // std::copy

int main() {
    //建立一個和輸出流緩衝區相關聯的疊代器
    std::ostreambuf_iterator<char> out_it(std::cout); // stdout iterator
    //向輸出流緩衝區中寫入字元元素
    *out_it = 'S';
    *out_it = 'T';
    *out_it = 'L';

    //和 copy() 函數連用
    std::string mystring("nhttp://c.biancheng.net/stl/");
    //將 mystring 中的字串全部寫入到輸出流緩衝區中
    std::copy(mystring.begin(), mystring.end(), out_it);
    return 0;
}
程式執行結果為:

STL
http://c.biancheng.net/stl/

有關 copy() 函數的具體用法,後續章節會做詳細講解。