C++11以前有很多原因不能提供一个通用的split,比如说需要考虑split以后的结果存储在什么类型的容器中,可以是vector、list等等包括自定义容器,很难提供一个通用的;再比如说需要split的源字符串很大的时候运算的时间可能会很长,所以这个split最好是lazy的,每次只返回一条结果。
C++11之前只能自己写,我目前发现的史上最优雅的一个实现是这样的:
void split(const string& s, vector& tokens, const string& delimiters = " ")
{
string::size_type lastPos = s.find_first_not_of(delimiters, 0);
string::size_type pos = s.find_first_of(delimiters, lastPos);
while (string::npos != pos || string::npos != lastPos) {
tokens.push_back(s.substr(lastPos, pos - lastPos));//use emplace_back after C++11 lastPos = s.find_first_not_of(delimiters, pos);
pos = s.find_first_of(delimiters, lastPos);
}
}
从C++11开始,标准库中提供了regex,regex用来做split就是小儿科了,比如:
std::string text = "Quick brown fox.";
std::regex ws_re("\\s+"); // whitespacestd::vector<:string> v(std::sregex_token_iterator(text.begin(), text.end(), ws_re, -1),
std::sregex_token_iterator());
for(auto&& s: v)
std::cout<
C++17提供的string_view可以加速上面提到的第一个split实现,减少拷贝,性能有不小提升,参看此文:Speeding Up string_view String Split Implementation。
从C++20开始,标准库中提供了ranges,有专门的split view,只要写str | split(' ')就可以切分字符串,如果要将结果搜集到vector中,可以这样用(随手写的,可能不是最简):
string str("hello world test split");
auto sv = str
| ranges::views::split(' ')
| ranges::views::transform([](auto&& i){
return i | ranges::to(); })
| ranges::to();
for(auto&& s: sv) {
cout<
}
其实C语言里面也有一个函数strtok用于char*的split,例如:
#include #include #include using namespace std;
int main()
{
string str = "one two three four five";
char *token = strtok(str.data(), " ");// non-const data() needs c++17 while (token != NULL) {
std::cout << token << '\n';
token = strtok(NULL, " ");
}
}
//如你所愿,输出如下:one
two
three
four
five
这里要注意的是strtok的第一个参数类型是char*而不是const char*,实际上strtok的确会改变输入的字符串。
参考文献: