[C++]文字列を任意の文字列で分割する
C++のstd::stringはC言語のchar[]と比べてすごく扱いやすいですが、それでもJavaや最近の言語と比べるとやはり機能は劣ります。
std::stringに文字列を任意の文字列で分割して配列やイテレータに変換するメソッドがないので、自分で作る必要があります。とは言っても、ループで回せば簡単に作れるので、そのサンプルを紹介します。
対応はC++11以降です。実際に使用する時は名前空間に注意してください。
forループで回す
#include <iostream>
#include <string>
using namespace std;
int main() {
string str("item1,item2,item3"), separator(",");
string tstr = str + separator;
long l = tstr.length(), sl = separator.length();
string::size_type pos = 0, prev = 0;
for (;pos < l && (pos = tstr.find(separator, pos)) != string::npos; prev = (pos += sl)) {
string item = tstr.substr(prev, pos - prev);
cout << item << endl;
}
return 0;
}
結果
$ ./a.out
item1
item2
item3
findメソッドでseparatorを検索し、見つかったら前回のseparatorの次の文字(初回は0文字目)から見つかったseparatorの前までの文字列をsubstrで取得します。
でも、そのままだと末尾の要素が取れないので、分割元の文字列の末尾にseparatorを追加してます。(末尾追加せずに行うと条件分岐が複雑になってしまうため、この手法をとった)
もちろんseparatorが複数文字からなる文字列でも問題ありません。
#include <iostream>
#include <string>
using namespace std;
int main() {
string str("item1,item2,item3"), separator("tem");
string tstr = str + separator;
long l = tstr.length(), sl = separator.length();
string::size_type pos = 0, prev = 0;
for (;pos < l && (pos = tstr.find(separator, pos)) != string::npos; prev = (pos += sl)) {
string item = tstr.substr(prev, pos - prev);
cout << item << endl;
}
return 0;
}
結果
% ./a.out
i
1,i
2,i
3
ちなみに、separatorを空文字列にしてしまうと無限ループに陥るのでご注意ください。
split関数
頻繁に使う場合は、文字列の配列を返すsplit関数を定義してもいいかもしれません。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<string> split(string str, string separator) {
if (separator == "") return {str};
vector<string> result;
string tstr = str + separator;
long l = tstr.length(), sl = separator.length();
string::size_type pos = 0, prev = 0;
for (;pos < l && (pos = tstr.find(separator, pos)) != string::npos; prev = (pos += sl)) {
result.emplace_back(tstr, prev, pos - prev);
}
return result;
}
int main() {
string str = "item1,item2,item3";
vector<string> ary = split(str, ",");
for (int i = 0; i < ary.size(); i++) {
cout << ary[i] << endl;
}
return 0;
}
結果
$ ./a.out
item1
item2
item3
こっちにはseparator==""
の判定入れました。
1文字ならstd::getlineを使った方が簡略に書ける