読者です 読者をやめる 読者になる 読者になる

kanetaiの二次記憶装置

プログラミングに関するやってみた、調べた系のものをQitaに移して、それ以外をはてブでやる運用にしようと思います。http://qiita.com/kanetai

Boost.Tokenizer

Boost.Tokenizer

C++でsplitを使いたいときに使う。
Boost String algorithms Libraryのほうでもいいかな。
w_charとかも使えるっぽい。
分割関数を定義すれば、マルチバイトでも使えそう。

名前空間/ヘッダ

boost

#include<boost/tokenizer.hpp>

boost::char_separator

セパレータでデリミタを指定する
とりあえず、テンプレートには、charを入れときゃいいみたい
○第一引数 const Char* dropped_delimiters
デリミタの集合を入れる。
○第二引数 const Char* kept_delimiters = ""
解析結果にトークンとして残すデリミタの集合。デフォで=""
○第三引数 empty_token_policy empty_tokens = drop_empty_tokens
切り詰めるかどうかの指定(空文字列をトークンとして扱うかどうか)。
boost::drop_empty_tokens か boost::keep_empty_tokensを指定。デフォで=drop_empty_tokens

boost::char_separator< char > 
	sep(" \t"), 
	sep1(" \t", "", boost::keep_empty_tokens), 
	sep2("\t", " ");

boost::tokenizer

コンストラクタとかassignで解析。
テンプレートにはとりあえずセパレータのタイプを入れときゃいいみたい。
第一引数に解析文字列、第二引数にセパレータを入れればいい。
※引数にiteratorを使うとか、TokenizerFunctionコンセプトを使うとかいろいろ使い方があるみたい

boost::tokenizer< boost::char_separator< char > > 
	tokens(original, sep), 
	tokens1(original, sep1), 
	tokens2(original, sep2);

boost::tokenizer::iterator

tokenizeされたtokensをたどるイテレータ

string original = " \tABC DEFG HIJKL MNOPQ RSTU \t VWXYZ \t";
boost::tokenizer< boost::char_separator< char > >::iterator it;

cout << "sep\t→";
for( it = tokens.begin(); it != tokens.end(); ++it ) cout << "[" << *it << "]";
cout<<endl;

cout << "sep1\t→";
for( it = tokens1.begin(); it != tokens1.end(); ++it ) cout << "[" << *it << "]";
cout<<endl;

cout << "sep2\t→";
for( it = tokens2.begin(); it != tokens2.end(); ++it ) cout << "[" << *it << "]";
cout<<endl;

結果

sep     →[ABC][DEFG][HIJKL][MNOPQ][RSTU][VWXYZ]
sep1    →[][][ABC][DEFG][HIJKL][MNOPQ][RSTU][][][VWXYZ][][]
sep2    →[ ][ABC][ ][DEFG][ ][HIJKL][ ][MNOPQ][ ][RSTU][ ][ ][VWXYZ][ ]

boost::offset_separator

オフセットを指定するタイプのセパレータ
○第一引数、第二引数 Iter bgegin, Iter end,
オフセットリストのbegin, endのiterator
○第3引数 wrap_offset = true
オフセットリストが繰り返して適用される。デフォでtrue
○第4引数 return_partial_last = true
最後にあまった部分を正常トークンとして認識する。デフォでtrue

std::string target = "2011112920111130wed";
int offsets[] = {4,2,2};
boost::offset_separator
	off_sep1( offsets, offsets+3 ),
	off_sep2( offsets, offsets+3, true, false),
	off_sep3( offsets, offsets+3, false, true);

boost::tokenizer< boost::offset_separator > 
	off_tokens1(target, off_sep1), 
	off_tokens2(target, off_sep2), 
	off_tokens3(target, off_sep3);

boost::tokenizer< boost::offset_separator >::iterator i;

std::cout << "off_sep1\t→";
for( i = off_tokens1.begin(); i != off_tokens1.end(); ++i ) std::cout << "[" << *i << "]";
std::cout << std::endl;

std::cout << "off_sep2\t→";
for( i = off_tokens2.begin(); i != off_tokens2.end(); ++i ) std::cout << "[" << *i << "]";
std::cout << std::endl;

std::cout << "off_sep3\t→";
for( i = off_tokens3.begin(); i != off_tokens3.end(); ++i ) std::cout << "[" << *i << "]";
std::cout << std::endl;

結果 あれ?第4引数の解釈が間違ってるのかな?

off_sep1        →[2011][11][29][2011][11][30][wed]
off_sep2        →[2011][11][29][2011][11][30][wed]
off_sep3        →[2011][11][29]

boost::escaped_list_separator >

コンストラクタ

explicit escaped_list_separator(Char e = '\\', Char c = ',', Char q = '\"' );

○第1引数 e: エスケープ文字
○第2引数 c: 区切り文字
○第3引数 q: 引用符文字
デフォルトでエスケープとか引用符を使ったCSV解析ができる。
Char->string_typeになった複数文字指定版のコンストラクタも定義されてる。

Tokenizer_TEST2.cpp
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>

int main(){
	std::string e="\\_", c=",.", q="\"'";
	std::string test_string[] = { "aaa,.bbb,ccc.ddd.", "aaa,.bbb\\,ccc_.ddd.", "\"aaa,.bbb,\",'ccc._,d\\\\dd.'" };
	boost::escaped_list_separator<char> esc, my_esc(e,c,q);
	std::cout << "my_esc:       e = {" << e << "} , c = {" << c << "} , q = {" << q <<"}" <<std::endl;

	BOOST_FOREACH(std::string str, test_string){
		std::cout << "test string = {" << str << "}" << std::endl
			<< "\t default->\t";
		boost::tokenizer< boost::escaped_list_separator<char> > tokens( str, esc ), my_tokens(str, my_esc);
		boost::tokenizer< boost::escaped_list_separator<char> >::iterator itr;
		for( itr = tokens.begin(); itr != tokens.end(); ++itr) std::cout << "{" << *itr << "}";
		std::cout << std::endl << "\t myesc->\t";
		for( itr = my_tokens.begin(); itr != my_tokens.end(); ++itr) std::cout << "{" << *itr << "}";
		std::cout << std::endl;
	}
	return 0;
}

結果
※普通そんなことしないだろうが、
エスケープ文字'\'でエスケープ文字'_'をエスケープするみたいなことをしたらエラーが出たので、
エスケープ文字は一つにしといたほうがいいと思う。

my_esc:       e = {\_} , c = {,.} , q = {"'}
test string = {aaa,.bbb,ccc.ddd.}
         default->      {aaa}{.bbb}{ccc.ddd.}
         myesc->        {aaa}{}{bbb}{ccc}{ddd}{}
test string = {aaa,.bbb\,ccc_.ddd.}
         default->      {aaa}{.bbb,ccc_.ddd.}
         myesc->        {aaa}{}{bbb,ccc.ddd}{}
test string = {"aaa,.bbb,",'ccc._,d\\dd.'}
         default->      {aaa,.bbb,}{'ccc._}{d\dd.'}
         myesc->        {aaa,.bbb,}{ccc.,d\dd.}

TokenizerFunction Concept

class
{
public:
	void reset()
	template<typename Iterator, typename Token>
		bool operator()(Iterator& next, Iterator end, Token& tok);
};

publicで上記のようなreset(), operator()を実装したクラスをBoost::Tokenizerに渡すことができる。
reset()はもし内部変数があればせれをリセットする
operator()は、nextで始まりendで終わる範囲からトークンを切り出してtokに格納する。
nextは次に読み込む位置まで前進させて、トークンが取り出せなかったらfalse、取り出せたらtrueを返す。

Tokenizer_TEST3.cpp
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
#include <cstdio>

//※正しいsjis文字列が入力されたかかどうかは判定してない
struct sjis_separator
{
	void reset(){} //もし内部変数があればそれをリセットする

	template<typename Iterator, typename Token>
		bool operator()(Iterator& itr, Iterator end, Token& tok)
		{
			tok = Token(); //tokをクリア
			if(itr == end) return false; //読めるトークンが無ければfalse
			
			unsigned char ch = *itr;
			tok = *itr++;
			if( 0x81 <= ch && ch <= 0x9f || 0xe0 <= ch && ch <= 0xef){
				if(itr == end) return false;
				tok += *itr++;
			}
			return true;
		}// operator()
}; //struct sjis_separator

int main(){
	//標準入力全体を分割
	std::cin.unsetf( std::ios_base::skipws ); //skip white spaceを解除
	std::string str( (std::istream_iterator<char>(std::cin)), std::istream_iterator<char>() );
	for(size_t i=0; i<str.size(); ++i) printf("%#x ",str[i]&0xff);
	puts("");

	boost::tokenizer<sjis_separator> tokens( str );
	boost::tokenizer<sjis_separator>::iterator itr;
	for(itr=tokens.begin(); itr != tokens.end(); ++itr) std::cout<<"["<<*itr<<"]";
	std::cout<<std::endl;
	std::cin.get();
	return 0;
}

動作確認

今のはメラゾーマではない...メラだ...
私の戦闘力は53万です
わしの波動球は108式まであるぞ
^Z
0x8d 0xa1 0x82 0xcc 0x82 0xcd 0x83 0x81 0x83 0x89 0x83 0x5d 0x81 0x5b 0x83 0x7d
0x82 0xc5 0x82 0xcd 0x82 0xc8 0x82 0xa2 0x2e 0x2e 0x2e 0x83 0x81 0x83 0x89 0x82
0xbe 0x2e 0x2e 0x2e 0xa 0x8e 0x84 0x82 0xcc 0x90 0xed 0x93 0xac 0x97 0xcd 0x82 0
xcd 0x35 0x33 0x96 0x9c 0x82 0xc5 0x82 0xb7 0xa 0x82 0xed 0x82 0xb5 0x82 0xcc 0x
94 0x67 0x93 0xae 0x8b 0x85 0x82 0xcd 0x31 0x30 0x38 0x8e 0xae 0x82 0xdc 0x82 0x
c5 0x82 0xa0 0x82 0xe9 0x82 0xbc 0xa
[][][][][][][][][][][][][.][.][.][][][][.][.][.][
][][][][][][][5][3][][][][
][][][][][][][][1][0][8][][][][][][][
]