12.05.2012

C++: Join the Strings, then Split into Integers

C++: 文字列を結合し、区切って数値へ変換する

動機

次のような数字と空白で構成された文字列(string)の vector がある。

{ "10", "2 3", "456", " 789 ", "0" }

これらを先頭から順に結合し、空白の位置で再度分割し、数値(int)の vector へ変換したい。

1. 文字列の結合

Scalaの心地よさに触発されたので、foldLeft関数を実装してみた。
自作のfoldLeftに対してSTL標準のplusファンクタ(Scalaで言う_+_)を適用することで結合を実現する。

template <typename Iterator, typename Tp, typename Func>
Tp foldLeft(Iterator first, Iterator last, Tp seed, Func f) {
  for (Iterator it = first; it != last; ++it) seed = f(seed, *it);
  return seed;
}

template <typename Container>
typename Container::value_type join(Container const& container) {
  typedef typename Container::value_type Tp;
  return foldLeft(container.begin(), container.end(), Tp(), std::plus<Tp>());
}

join関数の中で使っている Container::value_type は、typename キーワードを付けないとコンパイルが通らない。
テンプレート変数が型なのか値なのか、今回のようなケースではコンパイラは推論することができないためだ。

2. 文字列の分割

下記URL参照。stringstream と getline を利用した実装。

std::vector<std::string> split(std::string const& str, char delimiter = ' ') {
  std::istringstream iss(str);
  std::string buf;
  std::vector<std::string> result;
  while (std::getline(iss, buf, delimiter)) result.push_back(buf);
  return result;
}

3. 文字列から数値へ変換

こちらも Functional Programming ライクな map 関数を作成した。
std::map との名前の競合を避けるため fmap と命名したのは不本意であるが詮方無しか。

現状では std::vector から std::vector への変換しかできないが、他のコンテナへも適用可能にするなど
改善の余地はありそうだ。

テンプレート変数の Func は、argument_type および result_type という型名の定義を必要とする仕様とした。
通常は std::unary_function を継承すればよい。

template <typename Func>
std::vector<typename Func::result_type> fmap(
    std::vector<typename Func::argument_type> const& src) {
  std::vector<typename Func::result_type> result(src.size());
  std::transform(src.begin(), src.end(), result.begin(), Func());
  return result;
};

template <typename Arg, typename Result>
class toNumeric : public std::unary_function<Arg, Result> {
public:
  Result operator()(Arg const& str) const {
    Result result;
    std::istringstream iss(str);
    iss >> result;
    return result;
  }
};
typedef toNumeric<std::string, int> toInt;
typedef toNumeric<std::string, long long int> toLong;

個々の文字列から数値への変換は、toNumeric という独自のファンクタを作成した。
この処理でも stringstream を活用している。
toInt が int 型への変換、toLong が long long int 型への変換処理である。

コード全体

#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <functional>

// join
template <typename Iterator, typename Tp, typename Func>
Tp foldLeft(Iterator first, Iterator last, Tp seed, Func f) {
  for (Iterator it = first; it != last; ++it) seed = f(seed, *it);
  return seed;
}

template <typename Container>
typename Container::value_type join(Container const& container) {
  typedef typename Container::value_type Tp;
  return foldLeft(container.begin(), container.end(), Tp(), std::plus<Tp>());
}

// split
std::vector<std::string> split(std::string const& str, char delimiter = ' ') {
  std::istringstream iss(str);
  std::string buf;
  std::vector<std::string> result;
  while (std::getline(iss, buf, delimiter)) result.push_back(buf);
  return result;
}

// map
template <typename Func>
std::vector<typename Func::result_type> fmap(
    std::vector<typename Func::argument_type> const& src) {
  std::vector<typename Func::result_type> result(src.size());
  std::transform(src.begin(), src.end(), result.begin(), Func());
  return result;
};

template <typename Arg, typename Result>
class toNumeric : public std::unary_function<Arg, Result> {
public:
  Result operator()(Arg const& str) const {
    Result result;
    std::istringstream iss(str);
    iss >> result;
    return result;
  }
};
typedef toNumeric<std::string, int> toInt;
typedef toNumeric<std::string, long long int> toLong;

#define each(i,c) for (typeof((c).begin()) i=(c).begin(); i!=(c).end(); ++i)

using namespace std;

int main(int argc, char **argv) {
  string s[] = { "10", "2 3", "456", " 789 ", "0" };
  vector<string> vs(s, s + sizeof(s) / sizeof(s[0]));

  vector<int> vi = fmap<toInt>(split(join(vs)));
  each(i,vi) cout << *i << endl;
}

実行例

102
3456
789
0

 

References

Story of Your Life » Blog Archive » C++で文字列のsplit:
http://shnya.jp/blog/?p=195

0 件のコメント:

コメントを投稿