如何迭代由空格分隔的单词组成的字符串中的单词?
注意,我对C字符串函数或那种字符操作/访问不感兴趣。比起效率,我更喜欢优雅。我当前的解决方案:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
string s = "Somewhere down the road";
istringstream iss(s);
do {
string subs;
iss >> subs;
cout << "Substring: " << subs << endl;
} while (iss);
}
我的代码是:
#include <list>
#include <string>
template<class StringType = std::string, class ContainerType = std::list<StringType> >
class DSplitString:public ContainerType
{
public:
explicit DSplitString(const StringType& strString, char cChar, bool bSkipEmptyParts = true)
{
size_t iPos = 0;
size_t iPos_char = 0;
while(StringType::npos != (iPos_char = strString.find(cChar, iPos)))
{
StringType strTemp = strString.substr(iPos, iPos_char - iPos);
if((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
push_back(strTemp);
iPos = iPos_char + 1;
}
}
explicit DSplitString(const StringType& strString, const StringType& strSub, bool bSkipEmptyParts = true)
{
size_t iPos = 0;
size_t iPos_char = 0;
while(StringType::npos != (iPos_char = strString.find(strSub, iPos)))
{
StringType strTemp = strString.substr(iPos, iPos_char - iPos);
if((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
push_back(strTemp);
iPos = iPos_char + strSub.length();
}
}
};
例子:
#include <iostream>
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
DSplitString<> aa("doicanhden1;doicanhden2;doicanhden3;", ';');
for each (std::string var in aa)
{
std::cout << var << std::endl;
}
std::cin.get();
return 0;
}
我编写了以下代码。您可以指定分隔符,它可以是字符串。结果类似于Java的String.split,结果中包含空字符串。
例如,如果我们调用split(“ABCPICKABCANYABCTWO:ABC”,“ABC”),结果如下:
0 <len:0>
1 PICK <len:4>
2 ANY <len:3>
3 TWO: <len:4>
4 <len:0>
代码:
vector <string> split(const string& str, const string& delimiter = " ") {
vector <string> tokens;
string::size_type lastPos = 0;
string::size_type pos = str.find(delimiter, lastPos);
while (string::npos != pos) {
// Found a token, add it to the vector.
cout << str.substr(lastPos, pos - lastPos) << endl;
tokens.push_back(str.substr(lastPos, pos - lastPos));
lastPos = pos + delimiter.size();
pos = str.find(delimiter, lastPos);
}
tokens.push_back(str.substr(lastPos, str.size() - lastPos));
return tokens;
}
对于那些不愿意为代码大小牺牲所有效率并将“高效”视为一种优雅的人来说,以下内容应该是一个最佳选择(我认为模板容器类是一个非常优雅的添加):
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, length = str.length();
using value_type = typename ContainerT::value_type;
using size_type = typename ContainerT::size_type;
while(lastPos < length + 1)
{
pos = str.find_first_of(delimiters, lastPos);
if(pos == std::string::npos)
{
pos = length;
}
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不进行任何复制,从而提高了惊人的速度。
它的速度是这个页面上最快的tokenize的两倍多,几乎是其他页面的5倍。此外,使用完美的参数类型,您可以消除所有字符串和列表副本,以提高速度。
此外,它不执行结果的返回(效率极低),而是将令牌作为引用传递,因此也允许您根据需要使用多个调用来构建令牌。
最后,它允许您指定是否通过最后一个可选参数从结果中删除空标记。
它只需要std::string。。。其余的是可选的。它不使用流或boost库,但足够灵活,能够自然地接受这些外来类型。
#include <iostream>
#include <regex>
using namespace std;
int main() {
string s = "foo bar baz";
regex e("\\s+");
regex_token_iterator<string::iterator> i(s.begin(), s.end(), e, -1);
regex_token_iterator<string::iterator> end;
while (i != end)
cout << " [" << *i++ << "]";
}
IMO,这是最接近python的re.split()。有关regex_token_iterator的更多信息,请参阅cplusplus.com。-1(regex_token_iterator tor中的第四个参数)是序列中不匹配的部分,使用匹配作为分隔符。