std::regex_token_iterator
基于正则表达式执行通用的分词操作。对于单个字符的简单分割,它可能过于复杂,但它能够起作用而且并不太冗长:
std::vector<std::string> split(const string& input, const string& regex) {
// passing -1 as the submatch index parameter performs splitting
std::regex re(regex);
std::sregex_token_iterator
first{input.begin(), input.end(), re, -1},
last;
return {first, last};
}
regex_token_iterator
是 C++11 中定义的,但是直到 GCC 版本 4.9 才原生支持它(可参考这里)。 在早期版本的 GCC 中,您可以使用 Boost regex。 - Brent Bradburn\\s+
,用于匹配空格。此外,在 gcc 4.9 上,我必须显式地使用字符串参数初始化正则表达式,然后将其传递给迭代器构造函数。只需添加 regex re{regex_str};
作为第一行,其中 regex_str
是示例中称为 regex
的字符串,然后传递 re
即可。 - Alfred Bratterudstd::sregex_token_iterator...
开头的那一行的语法。这是两个名为first和last的迭代器吗?为什么last
没有被设置为任何值 - 我想应该有某种默认值...? - code_fodder这里是一个(可能更简洁的)基于您提到的帖子的字符串分割方法。
#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> split(const std::string &s, char delim) {
std::stringstream ss(s);
std::string item;
std::vector<std::string> elems;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
// elems.push_back(std::move(item)); // if C++11 (based on comment from @mchiasson)
}
return elems;
}
std::move
并不会移动任何东西,它只是将类型转换。item
在栈上定义,它将被复制到向量的堆上。因此,在这里使用 std::move
不会避免复制。 - Jack Zhangitem
在堆栈上定义,但内部数据指针指向在堆上分配的数据。通过使用std::move
,将选择push_back(std::string&&)
重载,导致向量中的std::string
对象通过移动进行初始化--仅复制数据指针,而不是复制整个缓冲区。 - Tyg13std::move
优化吗? - dshin以下是使用boost
将字符串拆分并填充提取元素的示例。
#include <boost/algorithm/string.hpp>
std::string my_input("A,B,EE");
std::vector<std::string> results;
boost::algorithm::split(results, my_input, boost::is_any_of(","));
assert(results[0] == "A");
assert(results[1] == "B");
assert(results[2] == "EE");
受其他答案启发,这是另一种正则表达式解决方案(链接),但希望更短且易于阅读:
std::string s{"String to split here, and here, and here,..."};
std::regex regex{R"([\s,]+)"}; // split on space and comma
std::sregex_token_iterator it{s.begin(), s.end(), regex, -1};
std::vector<std::string> words{it, {}};
words{it, {}};
的描述? - Gardenersregex_token_iterator
构造函数的 -1
参数会导致对象在匹配之间迭代片段。默认值 0
会迭代与正则表达式匹配的片段。更多详情请参见此处。 - xperroni我不知道这是否更简洁,但对于那些更熟悉动态语言(如javascript)的人来说,这可能更容易理解。它只使用了C++11的auto
和基于范围的for
循环特性。
#include <string>
#include <cctype>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
string s = "hello how are you won't you tell me your name";
vector<string> tokens;
string token;
for (const auto& c: s) {
if (!isspace(c))
token += c;
else {
if (token.length()) tokens.push_back(token);
token.clear();
}
}
if (token.length()) tokens.push_back(token);
return 0;
}
for (auto const c : s) {...}
? - Sebastian Mach我选择使用boost::tokenizer
,但是我没有进行大量数据的测试和处理。
这里是来自boost文档的示例代码,经过了Lambda修改:
#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>
#include <vector>
int main()
{
using namespace std;
using namespace boost;
string s = "This is, a test";
vector<string> v;
tokenizer<> tok(s);
for_each (tok.begin(), tok.end(), [&v](const string & s) { v.push_back(s); } );
// result 4 items: 1)This 2)is 3)a 4)test
return 0;
}
for (auto && s : tok) { v.push_back(s); }
。 - Drew Dormann#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
vector<string> split(const string& str, int delimiter(int) = ::isspace){
vector<string> result;
auto e=str.end();
auto i=str.begin();
while(i!=e){
i=find_if_not(i,e, delimiter);
if(i==e) break;
auto j=find_if(i,e, delimiter);
result.push_back(string(i,j));
i=j;
}
return result;
}
int main(){
string line;
getline(cin,line);
vector<string> result = split(line);
for(auto s: result){
cout<<s<<endl;
}
}
int
作为分隔符,而且为什么是 int delimiter(int)
中的 (int)
? - Ela782#include <string>
#include <vector>
#include <sstream>
inline vector<string> split(const string& s) {
vector<string> result;
istringstream iss(s);
for (string w; iss >> w; )
result.push_back(w);
return result;
}
s
重命名为w
后,它运行得很好。请更新答案以便在任何地方都可以编译。 - Carlos Pinzónresult.emplace_back(std::move(w));
- Кое Кто#include <string>
template<class Iter>
Iter splitStrings(const std::string &s, const std::string &delim, Iter out)
{
if (delim.empty()) {
*out++ = s;
return out;
}
size_t a = 0, b = s.find(delim);
for ( ; b != std::string::npos;
a = b + delim.length(), b = s.find(delim, a))
{
*out++ = std::move(s.substr(a, b - a));
}
*out++ = std::move(s.substr(a, s.length() - a));
return out;
}
一些测试用例:
void test()
{
std::vector<std::string> out;
size_t counter;
std::cout << "Empty input:" << std::endl;
out.clear();
splitStrings("", ",", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, empty delimiter:" << std::endl;
out.clear();
splitStrings("Hello, world!", "", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", no delimiter in string:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxya", "xyz", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string:" << std::endl;
out.clear();
splitStrings("abxycdxy!!xydefxya", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string"
", input contains blank token:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxya", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string"
", nothing after last delimiter:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxy", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", only delimiter exists string:" << std::endl;
out.clear();
splitStrings("xy", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
}
预期输出:
空输入: 0: 非空输入,空分隔符: 0: 你好,世界! 非空输入,非空分隔符,字符串中没有分隔符: 0: abxycdxyxydefxya 非空输入,非空分隔符,在字符串中存在分隔符: 0: ab 1: cd 2: !! 3: def 4: a 非空输入,非空分隔符,在字符串中存在分隔符,输入包含空标记: 0: ab 1: cd 2: 3: def 4: a 非空输入,非空分隔符,在字符串中存在分隔符,最后一个分隔符之后没有内容: 0: ab 1: cd 2: 3: def 4: 非空输入,非空分隔符,只有分隔符在字符串中存在: 0: 1:
这是我的答案。冗长、易读且高效。
std::vector<std::string> tokenize(const std::string& s, char c) {
auto end = s.cend();
auto start = end;
std::vector<std::string> v;
for( auto it = s.cbegin(); it != end; ++it ) {
if( *it != c ) {
if( start == end )
start = it;
continue;
}
if( start != end ) {
v.emplace_back(start, it);
start = end;
}
}
if( start != end )
v.emplace_back(start, end);
return v;
}