Hatena::ブログ(Diary)

Life like a clown このページをアンテナに追加 RSSフィード Twitter

2010-01-12

ストリームから読み込んで STL コンテナに格納する

今まで,std::istream から std::string 辺りに一括で読み込む関数を適当に書いていたのですが,これを少し真面目に実装してみます.最初は,std::getline(in, s, char_traits<char>::eof()) とかすれば良いのかなと思ったのですが,delim (第 3 引数)は char 型らしいのでこの方法では無理のようです.そこで,std::getline() の実装をベースに delim の判定部分を除いてみました.

#include <istream>
#include <iterator>

namespace clx {
    template <class Ch, class Tr, class Container>
    std::basic_istream<Ch, Tr>& read(std::basic_istream<Ch, Tr>& in, Container& dest) {
        typedef typename Container::value_type value_type;
        typedef typename Container::size_type size_type;
        typedef std::basic_istream<Ch, Tr> istream_type;
        typedef typename istream_type::ios_base ios_type;
        
        size_type extracted = 0;
        const size_type n = dest.max_size();
        typename ios_type::iostate err = ios_type::goodbit;
        typename std::basic_istream<Ch, Tr>::sentry se(in, true);
        if (se) {
            try {
                dest.clear();
                std::insert_iterator<Container> out(dest, dest.end());
                
                typename istream_type::int_type c = in.rdbuf()->sgetc();
                while (extracted < n && !Tr::eq_int_type(c, Tr::eof())) {
                    value_type elem;
                    if (in.rdbuf()->sgetn(reinterpret_cast<Ch*>(&elem), sizeof(value_type)) <= 0) {
                        err |= ios_type::failbit;
                        break;
                    }
                    *out = elem;
                    ++out;
                    ++extracted;
                    c = in.rdbuf()->sgetc();
                }
                if (Tr::eq_int_type(c, Tr::eof())) err |= ios_type::eofbit;
                else err |= ios_type::failbit; // larger than max_size
            }
            catch (...) {
                in.setstate(std::ios_base::badbit);
            }
        }
        
        if (!extracted) err |= ios_type::failbit;
        if (err) in.setstate(err);
        
        return in;
    }
}

std::string でなくとも max_size(), clear(), insert() を持つコンテナであれば dest (第 2 引数)として指定する事ができます.例えば,std::vector<int> を指定した場合は,sizeof(int) バイトずつ読み込んで,その結果を dest に追加していきます.

追記

後で思ったのですが,対象が std::string (と std::vector<char> 辺り)位なら以下でいけますね.

std::istreambuf_iterator<char> input(in);
std::istreambuf_iterator<char> last;
std::insert_iterator<std::string> out(dest, dest.end());
std::copy(input, last, out);