split - C 分割字符串?

  显示原文与译文双语对照的内容

在 C++ 中拆分字符串的最优雅的方法是什么? 字符串可以被假定为由空格分隔的单词组成。

( 注意我对C 字符串函数或者那种字符操作/访问不感兴趣) 。 另外,在你的回答中,请优先考虑优雅优于效率。

我现在最好的解决方案是:


#include <iostream>
#include <sstream>
#include <string>
using namespace std;

int main()
{
 string s("Somewhere down the road");
 istringstream iss(s);

 do
 {
 string sub;
 iss>> sub;
 cout <<"Substring:" <<sub <<endl;
 } while (iss);

}

时间:

FWIW,这是另一种从输入字符串提取令牌的方法,只依赖标准库工具。 它是设计STL背后的力量和优雅的一个例子。


#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>

int main() {
 using namespace std;
 string sentence ="And I feel fine...";
 istringstream iss(sentence);
 copy(istream_iterator<string>(iss),
 istream_iterator<string>(),
 ostream_iterator<string>(cout,"n"));
}

不用将提取的标记复制到输出流,可以使用相同的通用复制算法将它们插入到容器中。


vector<string> tokens;
copy(istream_iterator<string>(iss),
 istream_iterator<string>(),
 back_inserter(tokens));

。或者直接创建矢量:


vector<string> tokens{istream_iterator<string>{iss},
 istream_iterator<string>{}};

我使用这个命令来拆分字符串。 第一个将结果放在一个pre-constructed向量中,第二个向量返回一个新向量。


#include <string>
#include <sstream>
#include <vector>

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
 std::stringstream ss(s);
 std::string item;
 while (std::getline(ss, item, delim)) {
 elems.push_back(item);
 }
 return elems;
}


std::vector<std::string> split(const std::string &s, char delim) {
 std::vector<std::string> elems;
 split(s, delim, elems);
 return elems;
}


编辑: 注意这个解决方案不跳过空标记,因此下面会发现 4个项目,其中一个是空的:


std::vector<std::string> x = split("one:two::three", ':');

因为大家都在使用 Boost:


#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs,"string to split", boost::is_any_of("t"));

我打赌这比 stringstream 解决方案快得多。 因为这是一个通用的模板函数,它可以用各种分隔符来分割其他类型的字符串( 等等,或者 UTF-8 ) 。

有关详细信息,请参阅文档


#include <vector>
#include <string>
#include <sstream>

using namespace std;

int main()
{
 string str("Split me by whitespaces");
 string buf;//Have a buffer string
 stringstream ss(str);//Insert the string into a stream

 vector<string> tokens;//Create vector to hold our words

 while (ss>> buf)
 tokens.push_back(buf);
}

对于那些与谁它不坐设计实施了以牺牲所有代码长度和看"高效"作为一个类型的的外形设计,下面的应该的尝到甜头( 我认为模板容器类是一个awesomely优雅的添加。):


template <class ContainerT> 
void tokenize(const std::string& str, ContainerT& tokens,
 const std::string& delimiters ="", bool trimEmpty = false)
{
 std::string::size_type pos, lastPos = 0;

 using value_type = typename ContainerT::value_type;
 using size_type = typename ContainerT::size_type;

 while(true)
 {
 pos = str.find_first_of(delimiters, lastPos);
 if(pos == std::string::npos)
 {
 pos = str.length();

 if(pos!= lastPos ||!trimEmpty)
 tokens.push_back(value_type(str.data()+lastPos,
 (size_type)pos-lastPos ));

 break;
 }
 else
 {
 if(pos!= lastPos ||!trimEmpty)
 tokens.push_back(value_type(str.data()+lastPos,
 (size_type)pos-lastPos ));
 }

 lastPos = pos + 1;
 }
}

我通常选择使用 std::vector<std::string> 类型作为第二个参数( ContainerT ) 。 但 list<> 是为当不需要直接访问,速度比 vector<> 方式,甚至可以创建自己的字符串类,并使用像 std::list<subString>subString 哪不进行任何副本,以惊人的速度增加。

它比这个页面上最快的标记快一倍,而且速度比其他的快 5倍。 同时使用完美的参数类型,你可以消除所有的字符串和列表副本以增加额外的速度。

另外,它不执行结果的( 非常低效) 返回,而是将令牌作为引用传递,这样也允许你使用多个调用构建令牌,如果你愿意。

最后,它允许你指定是否通过最后一个可选参数来修剪结果中的空标记。

它只需要 std::string 。。 其余的是可选的,它不使用流或者boost库,但足够灵活,可以接受某些外部类型。


string line ="a line of text to iterate through";
string word;

istringstream iss(line, istringstream::in);

while( iss>> word ) 
{

...

}

这是我最喜欢的迭代字符串的方法。 你可以根据每个单词进行操作。

这类似于堆栈溢出问题 如何在 C++ 中标记一个字符串? 。


#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>

using namespace std;
using namespace boost;

int main(int argc, char** argv)
{
 string text ="token testtstring";

 char_separator<char> sep(" t");
 tokenizer<char_separator<char>> tokens(text, sep);
 BOOST_FOREACH(string t, tokens)
 {
 cout <<t <<"." <<endl;
 }
}

下面是另一个解决方案。它是紧凑且高效的:


void split(vector<string> &tokens, const string &text, char sep) {
 int start = 0, end = 0;
 while ((end = text.find(sep, start))!= string::npos) {
 tokens.push_back(text.substr(start, end - start));
 start = end + 1;
 }
 tokens.push_back(text.substr(start));
}

可以很容易地处理字符串分隔符,宽字符串等。

@sehe 在这里有一个更一般化的函数的版本。

我喜欢这样做,因为它将结果放入一个向量中,支持一个字符串作为一个下标,并控制对空值的控制。 但是,它看起来不那么好。



#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

vector<string> split(const string& s, const string& delim, const bool keep_empty = true) {
 vector<string> result;
 if (delim.empty()) {
 result.push_back(s);
 return result;
 }
 string::const_iterator substart = s.begin(), subend;
 while (true) {
 subend = search(substart, s.end(), delim.begin(), delim.end());
 string temp(substart, subend);
 if (keep_empty ||!temp.empty()) {
 result.push_back(temp);
 }
 if (subend == s.end()) {
 break;
 }
 substart = subend + delim.size();
 }
 return result;
}

int main() {
 const vector<string> words = split("So close no matter how far","");
 copy(words.begin(), words.end(), ostream_iterator<string>(cout,"n"));
}

当然,Boost有一个 split(),它可以部分工作。 使用分割的页面都进行 is_any_of() 作品great,由'white-space',而且,如果你能真正地意味着任何类型的white-space,.

又灵活又快速的方式


template<typename Operator>
void tokenize(Operator& op, const char* input, const char* delimiters) {
 const char* s = input;
 const char* e = s;
 while (*e!= 0) {
 e = s;
 while (*e!= 0 && strchr(delimiters, *e) == 0) ++e;
 if (e - s> 0) {
 op(s, e - s);
 }
 s = e + 1;
 }
}

要将它的与字符串矢量一起使用( 编辑: 因为有人指出不继承STL类。。 hrmf ;):


template<class ContainerType>
class Appender {
public:
 Appender(ContainerType& container) : container_(container) {;}
 void operator() (const char* s, unsigned length) { 
 container_.push_back(std::string(s,length));
 }
private:
 ContainerType& container_;
};

std::vector<std::string> strVector;
Appender v(strVector);
tokenize(v,"A number of words to be tokenized"," t");

就是这样这只是使用记号记号的一种方法,比如如何计算单词: !


class WordCounter {
public:
 WordCounter() : noOfWords(0) {}
 void operator() (const char*, unsigned) {
 ++noOfWords;
 }
 unsigned noOfWords;
};

WordCounter wc;
tokenize(wc,"A number of words to be counted"," t"); 
ASSERT( wc.noOfWords == 7 );

受到想象的限制;)

...