とあるC++の酷いtokenize処理 [C++]

| コメント(0) | トラックバック(0)

先日見つけた、tokenize処理のC++コード(元のコードの酷さを壊さない程度に整えてある)

void tokenize(
    std::vector< std::string >& result,
    const std::string& input,
    const char         delimiter
    )
{
  char*	buf = new (std::nothrow) char[ input.size() + 1 ] ;
  assert( buf != 0 ) ;

  buf[0] = '\0' ;
  char*	buf_ptr = buf ;

  for(std::string::const_iterator it = input.begin() ; it != input.end(); ++it ) {
    const char &c = *it;
    if( delimiter  c || '\r'  c || '\t' == c ) {
      *buf_ptr = '\0' ;
      if( buf[0] ) {
        result.push_back( buf );
        buf_ptr = buf ;
      }
    }
    else {
      *buf_ptr++ = *it ;
    }
  }

  *buf_ptr = '\0';
  if( buf[0] ) {
    result.push_back( buf ) ;
  }

  delete[] buf ;
}

関数始まった途端ツッコミどころである。

ていうか、それ Boost String Algorithms Library で出来るよ。とか、Boost使わないまでも、もっとスマートに書けるよね。 と思ったのでちょちょっと書いてみた。

- 指定されたdelimiter以外にも'\r''\t'もdelimiterになる。
- 連続したdelimiterは1つのdelimiterとして扱う。
- 文字列の先頭・末尾の(連続した)delimiterは無視する。

以上3点に気をつけつつ...

#include <algorithm>
#include <iostream>
#include <iterator>

#include <string>
#include <vector>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>

#include <cstdlib>
#include <cassert>


typedef std::vector<std::string> container_t;

// Boost.Algorithm
void tokenize_boost(
    container_t& result,
    const std::string& input,
    const char         delimiter
    )
{
  namespace algo = boost::algorithm;
  const char separator[] = { delimiter, '\r', '\t', '\0' };
  const std::string trimmed_input = algo::trim_copy_if(input, algo::is_any_of(separator));
  if ( trimmed_input.length() ) {
    algo::split(
        result,
        trimmed_input,
        algo::is_any_of(separator),
        algo::token_compress_on
        );
  }
}

// 標準ライブラリのみ
void tokenize_std(
    container_t& result,
    const std::string& input,
    const char         delimiter
    )
{
  const char separator[] = { delimiter, '\r', '\t', '\0' };
  std::string::size_type idx=0;
  while (1) {
    const std::string::size_type offset = input.find_first_not_of(separator, idx);
    if ( offset == std::string::npos )
      break;

    idx = input.find_first_of(separator, offset);
    if ( idx == std::string::npos ) {
      result.push_back(input.substr(offset));
      break;
    }
    result.push_back(input.substr(offset, idx-offset));
  }
}

// 問題のオリジナル版
void tokenize_original(
    container_t& result,
    const std::string& input,
    const char         delimiter
    )
{
  char*	buf = new (std::nothrow) char[ input.size() + 1 ] ;
  assert( buf != 0 ) ;

  buf[0] = '\0' ;
  char*	buf_ptr = buf ;

  for(std::string::const_iterator it = input.begin() ; it != input.end(); ++it ) {
    const char &c = *it;
    if( delimiter  c || '\r'  c || '\t' == c ) {
      *buf_ptr = '\0' ;
      if( buf[0] ) {
        result.push_back( buf ) ;
        buf_ptr = buf ;
      }
    }
    else {
      *buf_ptr++ = *it ;
    }
  }

  *buf_ptr = '\0';
  if( buf[0] ) {
    result.push_back( buf ) ;
  }

  delete[] buf ;
}

// --- ↓↓ 以下、お試しコード 
std::string escape(const std::string& org)
{
  std::string result(org);
  std::string::size_type idx = 0;
  while((idx = result.find_first_of("\r\t", idx)) != std::string::npos)
  {
    result.replace(idx, 1, result[idx] == '\r' ? "\\r" : "\\t");
    ++idx;
  }
  return result;
}
void tokenize_test(const std::string& input)
{
  std::vector< std::string > strvec;
  std::cout << escape(input) << "\n";

  tokenize_original(strvec, input, ',');
  std::copy(strvec.begin(), strvec.end(),
      std::ostream_iterator<std::string>(std::cout, "|"));
  std::cout << "/original\n";

  strvec.clear();
  tokenize_boost(strvec, input, ',');
  std::copy(strvec.begin(), strvec.end(),
      std::ostream_iterator<std::string>(std::cout, "|"));
  std::cout << "/boost\n";

  strvec.clear();
  tokenize_std(strvec, input, ',');
  std::copy(strvec.begin(), strvec.end(),
      std::ostream_iterator<std::string>(std::cout, "|"));
  std::cout << "/std\n\n";
}

int main() {
  std::vector< std::string > strvec;

  tokenize_test("\tabc,def\r\tghi\tjkl\t\r\r");
  tokenize_test("abc,def\r,,,\tghi\t\r\rjkl");
  tokenize_test("abcdefg");
  tokenize_test(",,,,,,,,,");

  return EXIT_SUCCESS;
}

後、tokenizeとは関係ないけど、同じプロジェクトの別の場所で定義されていた配列風クラスのコメントに 「std::vectorの模倣だけど(std::vectorとは違って)連続したメモリ領域を使うよ」(意訳) って書いてあった。
誰が書いたんか知らんけど、 ISO/IEC 14882:2003 の §23.2.4-1 を熟読することをおすすめしたい。

トラックバック(0)

トラックバックURL: http://floralcompany.jp/mt/mt-tb.cgi/3

コメントする

AUTHOR

  • turugina (虎王 剱奈)
  • E-mail: turugina {at} floralcompany.jp
  • pixiv
  • ニジエ

2014年5月

        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

アーカイブ

OpenID対応しています OpenIDについて
Powered by Movable Type 5.2.10

- 警 告 -

本サイトにはいわゆる「18禁画像」(イラスト)へのリンクが存在します。 未成年の方や、その手の画像に不快感を覚える方は、 該当記事(「えちぃの」及び「ちょっとえちぃの」カテゴリ) をご覧にならないようお願いいたします。

上記を理解した上で非表示のブログパーツを表示する
あわせて読みたいブログパーツ
ついった
drawr/pixiv/twitpic