2008年11月2日日曜日

getuntil.hpp

c++ の getline は、なんとなく改行コードまでを取得するという仕様で、ネットのプロトコル等で利用するには、不満が残ります。そこで、汎用的な以下のようなものが有用ではないか?と思い、以前に作成したものを貼っときます。

>>> getuntil.hpp

//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Copyright (c) 2007 OKI Miyuki (oki.miyuki at gmail dot com)
//
#include <iostream>
#include <string>

template<typename CharT, typename CharTraits, typename Allocator>
std::basic_istream<CharT, CharTraits>& getuntil(
std::basic_istream<CharT, CharTraits>& istrm,
std::basic_string<CharT, CharTraits, Allocator>& str,
const std::basic_string<CharT, CharTraits, Allocator>& delm
) {
typedef std::basic_istream<CharT, CharTraits> istrm_type;
std::ios_base::iostate cur_state = std::ios_base::goodbit;
bool read_once = false;
const typename istrm_type::sentry sync( istrm, true );
if( sync ) {
try {
str.clear();
typename CharTraits::int_type row_xor = 0;
typename CharTraits::int_type ins_xor = 0;
size_t delm_len = delm.size();
typename CharTraits::int_type val = istrm.rdbuf()->sgetc();
for( size_t i = 0; i < delm_len - 1; ++i, val = istrm.rdbuf()->snextc() ) {
if( CharTraits::eq_int_type( CharTraits::eof(), val ) ) break;
read_once = true;
ins_xor ^= val;
row_xor ^= CharTraits::to_int_type( delm[ i ] );
str += CharTraits::to_char_type( val );
}
row_xor ^= CharTraits::to_int_type( *delm.rbegin() );
for( size_t ipos = 0; ; val = istrm.rdbuf()->snextc(), ++ipos ) {
if( CharTraits::eq_int_type( CharTraits::eof(), val ) ) {
cur_state |= std::ios_base::eofbit;
break;
}
read_once = true;
ins_xor ^= val;
str += CharTraits::to_char_type( val );
if(
ins_xor == row_xor
&& val == CharTraits::to_int_type( *delm.rbegin() )
&& str.substr( ipos ) == delm
) {
istrm.rdbuf()->sbumpc();
break;
}
if( str.max_size() <= str.size() ) {
cur_state |= std::ios_base::failbit;
break;
}
ins_xor ^= CharTraits::to_int_type( str[ipos] );
}
} catch(...) {
istrm.setstate( std::ios_base::badbit, true );
}
}
if( !read_once ) {
cur_state |= std::ios_base::failbit;
}
istrm.setstate( cur_state );

return istrm;
}

template<typename CharT, typename CharTraits, typename Allocator>
std::basic_istream<CharT, CharTraits>& getuntil(
std::basic_istream<CharT, CharTraits>& istrm,
std::basic_string<CharT, CharTraits, Allocator>& str,
const CharT* delm
) {
std::basic_string<CharT,CharTraits,Allocator> delimiter( delm );
return getuntil( istrm, str, delimiter );
}


 使い方は、こんな感じです。

>>> getuntil.cpp

#include "getuntil.hpp"
#include <fstream>
#include <sstream>
#include <iomanip>

#include <boost/asio.hpp>

int main() {
std::ifstream ifs( "getuntil.cpp", std::ios::binary );

int i = 0;
std::string delm( "\r\n" );
std::string line;
while( ifs.good() ) {
getuntil( ifs, line, delm );
std::cout << std::setw(3) << ++i << ": " << line;
}

std::cout << std::endl;
std::cout << "Enter stringstream test" << std::endl;

std::stringstream ss;
ss << "high-ho, high-ho!";
ss << "It's home from work we go! high-ho, high-ho!...";

while( ss.good() ) {
getuntil( ss, line, "ho!" );
std::cout << line << std::endl;
}

std::cout << std::endl;
std::cout << "Enter boost::asio::streambuf test" << std::endl;

boost::asio::streambuf sb;

std::ostream ostrm( &sb );
ostrm << "This is data from client\r\n";
ostrm << "And flying send\r\n";

std::istream istrm( &sb );

while( istrm.good() ) {
getuntil( istrm, line, "\r\n" );
std::cout << line;
}
}

0 件のコメント: