我想从std::string
建立一个std::vector<std::string>
。
我可以使用std::stringsteam
,但想象一下有一个更短的方法:
std::string string_from_vector(const std::vector<std::string> &pieces) {
std::stringstream ss;
for(std::vector<std::string>::const_iterator itr = pieces.begin();
itr != pieces.end();
++itr) {
ss << *itr;
}
return ss.str();
}
我还能怎么做?
答案 0 :(得分:47)
std::string s;
for (std::vector<std::string>::const_iterator i = v.begin(); i != v.end(); ++i)
s += *i;
return s;
std::string s;
std::for_each(v.begin(), v.end(), [&](const std::string &piece){ s += piece; });
return s;
std::string s;
for (const auto &piece : v) s += piece;
return s;
不要使用std::accumulate
进行字符串连接,这是一个经典的Schlemiel the Painter's algorithm,甚至比在C中使用strcat
的常见示例更糟糕。 ++ 11移动语义,它为向量的每个元素产生两个不必要的累加器副本。即使使用移动语义,它仍然会为每个元素产生一个不必要的累加器副本。
以上三个例子是 O(n)。
对于字符串, std::accumulate
O(n²)。
你可以通过提供一个来为字符串设置
std::accumulate
O(n) 自定义仿函数:std::string s = std::accumulate(v.begin(), v.end(), std::string{}, [](std::string &s, const std::string &piece) -> decltype(auto) { return s += piece; });
请注意,
s
必须是对非const的引用,即lambda返回类型 必须是一个参考(因此decltype(auto)
),身体必须使用+=
不是+
。
在预期成为C ++ 20的当前草案中,std::accumulate
的定义在附加到累加器时altered使用std::move
,所以从C + +20以后,accumulate
对于字符串将 O(n),并且可以用作单行代码:
std::string s = std::accumulate(v.begin(), v.end(), std::string{});
答案 1 :(得分:35)
您可以使用<numeric>
标头中的std::accumulate()
标准函数(它可以正常工作,因为为operator +
定义了string
的重载,它返回了它的两个串联参数):
#include <vector>
#include <string>
#include <numeric>
#include <iostream>
int main()
{
std::vector<std::string> v{"Hello, ", " Cruel ", "World!"};
std::string s;
s = accumulate(begin(v), end(v), s);
std::cout << s; // Will print "Hello, Cruel World!"
}
或者,您可以使用更高效,更小的for
周期:
#include <vector>
#include <string>
#include <iostream>
int main()
{
std::vector<std::string> v{"Hello, ", "Cruel ", "World!"};
std::string result;
for (auto const& s : v) { result += s; }
std::cout << result; // Will print "Hello, Cruel World!"
}
答案 2 :(得分:8)
为什么不使用operator +将它们一起添加?
std::string string_from_vector(const std::vector<std::string> &pieces) {
return std::accumulate(pieces.begin(), pieces.end(), std::string(""));
}
std :: accumulate默认使用std :: plus,并且在C ++中添加两个字符串是连接,因为std :: string重载了operator +。
答案 3 :(得分:6)
我个人的选择是基于范围的for循环,如Oktalist's answer。
Boost也提供了一个很好的解决方案:
#include <boost/algorithm/string/join.hpp>
#include <iostream>
#include <vector>
int main() {
std::vector<std::string> v{"first", "second"};
std::string joined = boost::algorithm::join(v, ", ");
std::cout << joined << std::endl;
}
打印:
第一,第二
无论如何,我发现std::accumulate()
方法滥用了该算法(无论复杂性问题如何)。
答案 4 :(得分:2)
派对有点晚了,但我喜欢我们可以使用初始化列表的事实:
std::string join(std::initializer_list<std::string> i)
{
std::vector<std::string> v(i);
std::string res;
for (const auto &s: v) res += s;
return res;
}
然后你可以简单地调用(Python风格):
join({"Hello", "World", "1"})
答案 5 :(得分:2)
Google Abseil有功能absl :: StrJoin可以满足您的需求。
来自header文件的示例。
请注意,分隔符也可以是""
// std::vector<std::string> v = {"foo", "bar", "baz"};
// std::string s = absl::StrJoin(v, "-");
// EXPECT_EQ("foo-bar-baz", s);
答案 6 :(得分:0)
使用c ++ 11, stringstream 的方式并不太可怕:
#include <vector>
#include <string>
#include <algorithm>
#include <sstream>
#include <iostream>
int main()
{
std::vector<std::string> v{"Hello, ", " Cruel ", "World!"};
std::stringstream s;
std::for_each(begin(v), end(v), [&s](const std::string &elem) { s << elem; } );
std::cout << s.str();
}
答案 7 :(得分:0)
如果不需要尾随空格,请将accumulate
中定义的<numeric>
与自定义连接lambda一起使用。
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
int main() {
vector<string> v;
string s;
v.push_back(string("fee"));
v.push_back(string("fi"));
v.push_back(string("foe"));
v.push_back(string("fum"));
s = accumulate(begin(v), end(v), string(),
[](string lhs, const string &rhs) { return lhs.empty() ? rhs : lhs + ' ' + rhs; }
);
cout << s << endl;
return 0;
}
输出:
fee fi foe fum