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

<stx/cstring>

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

c_tokenizer<class charT>

Cで用いられる文字列(char*, unsigned char*, wchar_t*)をトークン分割するクラスです。テンプレート引数にはchar, unsigned char, wchar_tを与えることができ、それぞれSBCS(ascii), MBCS(Shift-JIS/EUC), DBCS(UNICODE)に対応したトークン分割を行ないます。

※ c_tokenizer はトークン分割対象および区切り文字集合のコピーを生成しません。したがって、c_tokenizer を利用している間にそれら2つの文字列の内容が変更されたときの動作は保証されません。

template<class charT>  
class c_tokenizer {
public:
  typedef token_traits<charT> traits;
  typedef traits::value_type value_type;
  typedef traits::pointer pointer;
  typedef traits::const_pointer const_pointer;
  typedef traits::range_type range_type;
  typedef traits::size_type size_type;
  explicit c_tokenizer(const_pointer str =0, const_pointer del=0, bool ret=false)
    : str_(str), del_(del), cur_(str_), ret_(ret) {}
  bool       empty();
  pointer    next();
  range_type next_range();
  pointer    substr(range_type range);
  size_type  count() const;
  void set_str(const_pointer str);
  void set_delimiter(const_pointer del, bool ret =false);
private:
  const_pointer str_;
  const_pointer del_;
  const_pointer cur_;
  bool          ret_;
  void skip();
};
// sample
// "apple,banana,cherry" を "," で区切る
stx::c_tokenizer<char> tok("apple,banana,cherry", ",");
while ( !tok.empty() ) {
  char* token = tok.next();
  cout << token << " ";
  delete[] token;
}
cout << endl;

c_tokenizer (コンストラクタ)

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

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

// sample
// "apple_banana__cherry_" を "_" で区切る
bool ret = false;
stx::c_tokenizer<char> tok("apple_banana__cherry_", "_", ret);
while ( !tok.empty() ) {
  char* token = tok.next();
  cout << token << " ";
  delete[] token;
}
cout << endl;

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

※ Shift-JIS, EUS などのマルチバイト文字列を分割するときは、stx::c_tokenizer<unsigned char> を用います。このとき、文字列はすべて unsigned char* となります。したがって必要に応じて char -> unsigned char のキャストが必要になります。

// sample
// "りんご、みかん、バナナ" を "、" で区切る
typedef unsigned char uchar;
stx::c_tokenizer<uchar> tok((uchar)"りんご、みかん、バナナ", (uchar)"、");
while ( !tok.empty() ) {
  uchar* token = tok.next();
  cout << token << " ";
  delete[] token;
}
cout << endl;

skip (private)

template<class charT>
void c_tokenizer<charT>::skip() {
  if ( !ret_ && cur_ )
    cur_ += traits::span(cur_,del_);
}

empty

template<class charT>
bool c_tokenizer<charT>::empty() {
  skip();
  return !cur_ || *cur_ == charT(0);
}

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

next

template<class charT>
c_tokenizer<charT>::pointer c_tokenizer<charT>::next() {
  return substr(next_range());
}

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

※ 切り出されたトークンはヒープ領域から operator new によって取得した領域です。したがって、得られた文字列は明示的に delete[] しなければなりません。

next_range

template<class charT>
c_tokenizer<charT>::range_type c_tokenizer<charT>::next_range() {
  skip();
  const_pointer start = cur_;
  cur_ += traits::cspan(cur_, del_);
  if ( ret_ && start == cur_ && traits::span(cur_,del_) )
    cur_ = traits::advance(cur_);
  return range_type(start-str_, cur_-start);
}

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

substr

template<class charT>
c_tokenizer<charT>::pointer c_tokenizer<charT>::substr(range_type range) {
  pointer p = new value_type[range.second+1];
  for ( int i = 0; i < range.second; ++i )
    p[i] = str_[range.first + i];
  p[range.second] = value_type(0);
  return p;
}

引数 range が表すトークン文字列を返します。

※ 切り出されたトークンはヒープ領域から operator new によって取得した領域です。したがって、得られた文字列は明示的に delete[] しなければなりません。

set_str

template<class charT>
void c_tokenizer<charT>::set_str(const_pointer str) {
  str_ = str;
  cur_ = str;
}

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

set_delimiter

template<class charT>
void c_tokenizer<charT>::set_delimiter(const_pointer del, bool ret) {
  del_ = del;
  ret_ = ret;
}

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

count

template<class charT>
c_tokenizer<charT>::size_type c_tokenizer<charT>::count() const {
  size_t count = 0;
  const_pointer currpos = cur_;
  while ( *currpos ) {
    if ( !ret_ )
      currpos += traits::span(currpos, del_);      
    if ( !*currpos )
      break;
    const_pointer start = currpos;
    if ( *currpos )
    currpos += traits::cspan(currpos, del_);
    if ( ret_ && start == currpos && traits::span(currpos,del_) )
      currpos = traits::advance(currpos);
    ++count;
  } 
  return count;
}

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

st_tokenizer / mbs_tokenizer / wcs_tokenizer

typedef c_tokenizer<char>          str_tokenizer;
typedef c_tokenizer<unsigned char> mbs_tokenizer;
typedef c_tokenizer<wchar_t>       wcs_tokenizer;

使用頻度の多い c_tokenizer<char>, c_tokenizer<unsigned char> および c_tokenizer<wchar_t> をそれぞれ str_tokenizer, mbs_tokenizer, wcs_tokenizerとtypedefしました。

(実装メモ : traitsについて)

異なる文字セット(SBCS,MBCS,DBCS)に対応したc_tokenizerをひとつの実装で実現するため、traitsと呼ばれるテクニックを用いました。

たとえば文字列の中から区切り文字を探し出すのに、 SBCSならstrspn / MSCSなら_mbsspn / DBCSならwcsspn を呼ばなければなりません。このとき、traits と呼ばれるヘルパ関数の集合を定義し、それらtraitsのヘルパ関数を呼び出すことで文字セットに応じた関数の呼び分けを実現しています。

template<class charT> struct token_traits {};

// traits for SBCS(ascii)
template<> struct token_traits<char> {
  typedef char value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef size_t size_type;
  typedef std::pair<size_type,size_type> range_type;
  static size_type span(const_pointer x, const_pointer y) 
    { return strspn(x,y); }
  static size_type cspan(const_pointer x, const_pointer y) 
    { return strcspn(x,y); }
  static const_pointer advance(const_pointer x) 
    { return x+1; }
};
  
// traits for MBCS(Shift-JIS, EUC)
template<> struct token_traits<unsigned char> {
  typedef unsigned char value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef size_t size_type;
  typedef std::pair<size_type,size_type> range_type;
  static size_type span(const_pointer x, const_pointer y) 
    { return _mbsspn(x,y); }
  static size_type cspan(const_pointer x, const_pointer y) 
    { return _mbscspn(x,y); }
  static const_pointer advance(const_pointer x) 
    { return _mbsinc(x); }
};
  
// traits for DBCS(UNICODE)
template<> struct token_traits<wchar_t> {
  typedef wchar_t value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef size_t size_type;
  typedef std::pair<size_type,size_type> range_type;
  static size_type span(const_pointer x, const_pointer y) 
    { return wcsspn(x,y); }
  static size_type cspan(const_pointer x, const_pointer y) 
    { return wcscspn(x,y); }
  static const_pointer advance(const_pointer x) 
    { return x+1; }
};