株式会社エス・スリー・フォー

<stx/string>

DDJ-J 1998年5月号で紹介したSTL拡張コンポーネント”STX”の改版です。C Magazine Jan 2000で公開したtokenizerをreviseしました。

tokenizer<class String>

標準C++ライブラリが提供する文字列クラス basic_string<charT> をトークン分割するクラスです。テンプレート引数にはstd::string もしくは std::wstring を与えることができます。

※ マルチバイト文字(Shift-JIS, EUCなど)は正しく分割できません。一旦DBCS(UNICODE)に変換しstd::wstringで扱うか、もしくは stx::c_tokenizer<unsigned char> をお使いください。

template<class String>
class tokenizer {
private:
  String::size_type cur_;
  String str_;
  String del_;
  bool   ret_;
  void skip();
public:
  typedef std::pair<String::size_type,String::size_type> range_type;
  tokenizer(const String& str, const String& del, bool ret=false);
  bool empty();
  String next();
  range_type next_range();
  void set_str(const String& str);
  void set_delimiter(const String& del, bool ret=false);
  size_t count() const;
};
// sample
// "apple,banana,cherry" を "," で区切る
stx::tokenizer<string> tok("apple,banana,cherry", ",");
while ( !tok.empty() ) {
  cout << tok.next() << " ";
}
cout << endl;

tokenizer (コンストラクタ)

template<class String>
tokenizer<String>::tokenizer(const String& str, const String& del, bool ret)
  : cur_(0), str_(str), del_(del), ret_(ret) {}
str
:トークン分割対象となる文字列
del
:トークンを区切る文字の集合
ret
:区切り文字をトークンとして扱うならtrue

ret == true のとき、区切り文字をトークンとして扱います。たとえば、

// sample
// "apple_banana__cherry_" を "_" で区切る
bool ret = false;
stx::tokenizer<string> tok("apple_banana__cherry_", "_", ret);
while ( !tok.empty() ) {
  cout << tok.next() << " ";
}
cout << endl;

では “apple”, “banana”, “cherry” が切り出されますが、ret = true とすると、”apple”, “_”, “banana”, “_”, “_”, “cherry”, “_” が切り出されます。

skip (private)

template<class String>
void tokenizer<String>::skip() {
  if ( cur_ == str_.length() )
    cur_ = String::npos;
  if ( !ret_ && cur_ != String::npos ) {
    String::size_type tmp = str_.find_first_not_of(del_, cur_);
    if ( tmp != String::npos )
      cur_ = tmp;
  }
}

empty

template<class String>
bool tokenizer<String>::empty() {
  skip();
  return cur_ == String::npos;
}

切り出すトークンが見つからないとき true を返します。

next

template<class String>
String tokenizer<String>::next() {
  range_type range = next_range();
  return str_.substr(range.first, range.second);
}

切り出されたトークンを返します。

next_range

template<class String>
tokenizer<String>::range_type tokenizer<String>::next_range() {
  skip();
  String::size_type start = cur_;
  String::size_type tmp = cur_;
  if ( cur_ != String::npos ) cur_ = str_.find_first_of(del_,cur_);
  if ( cur_ == String::npos ) return range_type(start,str_.length()-start);
  if ( ret_ && start == cur_ && del_.find(str_[cur_]) != String::npos ) ++cur_;
  return range_type(start,cur_-start);
}

トークンを切り出し、その位置と長さを表す std::pair<String::size_type,String::size_type> range を返します。range.first はトークンの開始位置、range.second はトークンの長さです。

set_str

template<class String>
void tokenizer<String>::set_str(const String& str) {
  str_ = str;
  cur_ = 0;
}

トークン分割対象となる文字列を設定します。トークン分割の開始位置を0に設定します。

set_delimiter

template<class String>
void tokenizer<String>::set_delimiter(const String& delim, bool ret) {
  del_ = delim;
  ret_ = ret;
}

区切り文字の集合を設定します。区切り文字をトークンとして扱うときは第2引数を true としてください。

count

template<class String>
size_t tokenizer<String>::count() const {
  size_t count = 0;
  String::size_type currpos = cur_;
  while ( currpos != String::npos ) {
    if ( !ret_ ) {
      currpos = str_.find_first_not_of(del_,currpos);
      if ( currpos == String::npos ) { ++count; break; }
    } else if ( currpos == str_.length() ) {
      break;
    }
    String::size_type start = currpos;
    if ( currpos != String::npos ) currpos = str_.find_first_of(del_,currpos);
    if ( currpos == String::npos ) { ++count; break; }
    if ( ret_ && start == currpos && del_.find(str_[currpos]) != String::npos ) ++currpos;
    ++count;
  }
  return count;
}

現在の位置から切り出されるトークンの数を返します。

string_tokenizer / wstring_tokenizer

typedef tokenizer<std::string>     string_tokenizer;
typedef tokenizer<std::wstring>    wstring_tokenizer;

使用頻度の多いtokenizer<std::string> および tokenizer<std::wstring>をそれぞれ string_tokenizer,wstring_tokenizerとtypedefしました。