C++: 文字列を結合し、区切って数値へ変換する
動機
次のような数字と空白で構成された文字列(string)の vector がある。
{ "10", "2 3", "456", " 789 ", "0" } |
これらを先頭から順に結合し、空白の位置で再度分割し、数値(int)の vector へ変換したい。
1. 文字列の結合
Scalaの心地よさに触発されたので、foldLeft関数を実装してみた。
自作のfoldLeftに対してSTL標準のplusファンクタ(Scalaで言う_+_)を適用することで結合を実現する。
1 2 3 4 5 6 7 8 9 10 11 | 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 を利用した実装。
1 2 3 4 5 6 7 | 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 を継承すればよい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 型への変換処理である。
コード全体
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #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; } |
実行例
1 2 3 4 | 102 3456 789 0 |
References
Story of Your Life » Blog Archive » C++で文字列のsplit:
http://shnya.jp/blog/?p=195
0 件のコメント:
コメントを投稿