C++のstringstreamでsplit的なものを書こうとしてハマった話
C++初心者がpythonとかにあるsplit関数的なものを書いたときの自分用備忘録。
istringstremの仕様をちゃんと理解していなくて、文字列の最後にスペースやタブがある場合にアタフタしたって話だゾ。
C++は赤ちゃんレベルなので間違っているポイントあればマサカリをオナシャス!
環境
何がしたい?
"aaa bbb ccc"
みたいな文字列から
["aaa", "bbb", "ccc"]
みたいなリストに変換したいだけ。
事前に要素数がわかれば簡単だけど、要素数がわからない or そもそも要素数を調べたいっていう状況。
istringstremを使う
google先生に聞いた結果、こんな実装が見つかった。
#include <iostream> #include <sstream> #include <vector> int main() { std::string line = "aaa bbb ccc"; std::vector<std::string> vec; std::string str; std::istringstream iss(line); while (!iss.eof()) { iss >> str; vec.push_back(str); } // output std::cout << "vector length : " << std::to_string(vec.size()) << std::endl; for (auto s : vec) std::cout << s << std::endl; return 0; }
出力↓
vector length : 3 aaa bbb ccc
なんだか動いてそうでヨシ!
文字列として最後にスペースがあるクソ文字列を突うずるっ込んってみる。
上のコードで、文字列を↓に変える
std::string line = "aaa bbb ccc ";
出力↓
vector length : 4 aaa bbb ccc ccc
クゥーン…
\tや\nにしても同様に最後の要素が重複して、ほんとひで
文字列が末端のときに読み込んで初めてeofがtrueになるって仕様なんですね。
getlineを使う
再びgoogle先生に聞いたらgetlineを使う方法がモリモリ出てきた。
#include <iostream> #include <sstream> #include <vector> int main() { std::string line = "aaa bbb ccc"; std::vector<std::string> vec; std::string str; std::stringstream ss(line); while (getline(ss, str, ' ')) { vec.push_back(str); } // output std::cout << "vector length : " << std::to_string(vec.size()) << std::endl; for (auto s : vec) std::cout << s << std::endl; return 0; }
出力↓
vector length : 3 aaa bbb ccc
ヨシ!
文字列の末尾にスペースを入れてみる
std::string line = "aaa bbb ccc ";
出力↓
vector length : 3 aaa bbb ccc
やったぜ。
おまけ; pythonの場合
line = 'aaa bbb ccc' vec = line.split() print('vector length : ' + str(len(vec))) [print(s) for s in vec]
やpn1
5/23 追記
getlineでやる方法はパッと見うまくいくんだけど、スペースが二個あると空文字がベクトルに紛れ込んでしまう。
じゃけん、ダサいけどif文で空文字つまみ出しましょうねー。
while (getline(ss, str, ' ')) { if (!str.empty()) vec.push_back(str); }
参考
http://risuo.hatenablog.com/entry/20120719/1342674411
https://qiita.com/tarokun/items/526c6d65d5b3024d6caa