Streambuf Iterator

プログラミング関係の話題を取り上げるとき,しばしばサンプルコードを載せることがある訳ですが,このときに問題になるのがタグ文字などの特殊文字.具体的には,“<”,“>”,“&”,“"”はそれぞれ“&lt;”,“&gt;”,“&amp;”,“&quot;”に変換する必要があります.これを手作業で行うのはさすがに面倒なので,一括変換してくれるプログラムが欲しくなります.私は,S34:Making of C++ Technical Documentsで紹介されているプログラムを利用しています.

さて,この変換プログラムは以下のようになります.

#include <iostream>
#include <iterator>
#include <string>
#include <map>

template <class InIter, class OutIter>
OutIter src2html(InIter first, InIter last, OutIter result)
{
    typedef InIter input_iterator;
    typedef typename input_iterator::char_type char_type;
    typedef typename input_iterator::traits_type traits_type;
    typedef std::basic_string<char_type, traits_type> string_type;
    typedef std::map<char_type, string_type> escmap;
    
    escmap esc;
    
    esc['&'] = "&amp;";
    esc['"'] = "&quot;";
    esc['<'] = "&lt;";
    esc['>'] = "&gt;";
    
    while (first != last) {
        char_type c = *first++;
        typename escmap::iterator pos = esc.find(c);
        if (pos != esc.end()) {
            result = std::copy(pos->second.begin(), pos->second.end(), result);
        }
        else *result++ = c;
    }
    
    return result;
}

int main(int argc, char* argv[])
{
    std::ostreambuf_iterator<char> result(std::cout);
    std::istreambuf_iterator<char> input(std::cin);
    std::istreambuf_iterator<char> last;
    
    src2html(input, last, result);
    
    return 0;
}

標準入力から一文字ずつ読み取り,変換しなければならない文字なら対応する文字列を出力し,それ以外の文字はそのまま出力するという流れになっています.ここで面白いのが,src2htmlはイテレータで受け取っているという事.このプログラム,メイン関数を以下のように書き換えても問題なく動作します.

int main(int argc, char* argv[])
{
    std::ostreambuf_iterator<char> result(std::cout);
    std::string s = "#include <iostream>";
    
    src2html(s.begin(), s.end(), result);
    
    return 0;
}

つまり,制御関数(src2html)を実装する際には,イテレータを用いることによって,メイン関数からの入力(文字列が来るのか,ストリームから読み取らなければならないのか)を意識せずに実装することができます(実際,algorithmヘッダファイルで提供される関数もこの形で実装されています).

それにしても,istreambuf_iterator/ostreambuf_iteratorというクラスが用意されていることは知りませんでした.C++ ライブラリ クイックリファレンスによると,

istreambuf_iteratorクラステンプレートは、ストリームバッファから文字を読み取るための入力反復子として、ストリームバッファオブジェクト(basic_streambufのインスタンス)をラッピングする。


C++ ライブラリ クイックリファレンス p.261

とのことですが,なるほどやり易いようにいろいろと用意されているのですね.C++もまだまだ知らないことが多いです.そのうち,リファレンスも一度全部目を通してみようかと思います.