如何将一个stringvector内插到一个string(优雅的方式)
我正在寻找最优雅的方式来把一个stringvector插入到一个string中。 以下是我现在使用的解决scheme:
static std::string& implode(const std::vector<std::string>& elems, char delim, std::string& s) { for (std::vector<std::string>::const_iterator ii = elems.begin(); ii != elems.end(); ++ii) { s += (*ii); if ( ii + 1 != elems.end() ) { s += delim; } } return s; } static std::string implode(const std::vector<std::string>& elems, char delim) { std::string s; return implode(elems, delim, s); }
那里有其他人吗?
这可能不是很明显,但在implode
添加string时,您正在执行大量的内存分配和释放操作。 一个可能的改进是为一次reserve()
,然后是所有的添加。
使用boost::algorithm::join(..)
:
#include <boost/algorithm/string/join.hpp> ... std::string joinedString = boost::algorithm::join(elems, delim);
另见这个问题 。
你应该使用std::ostringstream
而不是std::string
来构build输出(然后你可以在最后调用它的str()
方法来得到一个string,所以你的接口不需要改变,只是临时s
)。
从那里,你可以改变使用std::ostream_iterator
,就像这样:
copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim));
但是这有两个问题:
-
delim
现在需要是一个const char*
,而不是一个char
。 没什么大不了。 -
std::ostream_iterator
在每个元素(包括最后一个元素)之后写入分隔符。 所以你最后可能需要清除最后一个,或者编写自己的没有这个烦恼的迭代器版本。 如果你有很多代码需要这样的事情,那么值得做后者; 否则整个混乱可能是最好的避免(即使用ostringstream
而不是ostream_iterator
)。
std::vector<std::string> strings; const char* const delim = ", "; std::ostringstream imploded; std::copy(strings.begin(), strings.end(), std::ostream_iterator<std::string>(imploded, delim));
(包括<string>
, <vector>
, <sstream>
和<iterator>
)
如果你想有一个干净的结束(没有尾随分隔符)看看这里
string join(const vector<string>& vec, const char* delim) { stringstream res; copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim)); return res.str(); }
因为我喜欢单行(对于各种奇怪的东西非常有用,就像你会看到的那样),下面是一个使用std :: accumulate和C ++ 11 lambda的解决scheme:
std::accumulate(alist.begin(), alist.end(), std::string(), [](const std::string& a, const std::string& b) -> std::string { return a + (a.length() > 0 ? "," : "") + b; } )
我发现这个语法对于stream操作符很有用,我不希望在stream操作中出现各种奇怪的逻辑,只是做一个简单的string连接。 例如,考虑使用stream操作符(使用std;)来格式化string的方法的return语句:
return (dynamic_cast<ostringstream&>(ostringstream() << "List content: " << endl << std::accumulate(alist.begin(), alist.end(), std::string(), [](const std::string& a, const std::string& b) -> std::string { return a + (a.length() > 0 ? "," : "") + b; } ) << endl << "Maybe some more stuff" << endl )).str();
一个使用std::accumulate
版本:
#include <numeric> #include <iostream> #include <string> struct infix { std::string sep; infix(const std::string& sep) : sep(sep) {} std::string operator()(const std::string& lhs, const std::string& rhs) { std::string rz(lhs); if(!lhs.empty() && !rhs.empty()) rz += sep; rz += rhs; return rz; } }; int main() { std::string a[] = { "Hello", "World", "is", "a", "program" }; std::string sum = std::accumulate(a, a+5, std::string(), infix(", ")); std::cout << sum << "\n"; }
这是另一个不添加最后一个元素后面的分隔符:
std::string concat_strings(const std::vector<std::string> &elements, const std::string &separator) { if (!elements.empty()) { std::stringstream ss; auto it = elements.cbegin(); while (true) { ss << *it++; if (it != elements.cend()) ss << separator; else return ss.str(); } } return "";
特别是对于更大的集合,你想避免检查你是否仍然添加第一个元素或不确保没有尾随分隔符…
所以对于空元素或单元素列表,根本就没有迭代。
空的范围是微不足道的:return“”。
单元素或多元素可以通过accumulate
完美处理:
auto join = [](const auto &&range, const auto separator) { if (range.empty()) return std::string(); return std::accumulate( next(begin(range)), // there is at least 1 element, so OK. end(range), range[0], // the initial value [&separator](auto result, const auto &value) { return result + separator + value; }); };
运行示例( 需要C ++ 14 ): http : //cpp.sh/8uspd
稍长的解决scheme,但不使用std::ostringstream
,并不需要一个破解删除最后一个分隔符。
和代码:
struct appender { appender(char d, std::string& sd, int ic) : delim(d), dest(sd), count(ic) { dest.reserve(2048); } void operator()(std::string const& copy) { dest.append(copy); if (--count) dest.append(1, delim); } char delim; mutable std::string& dest; mutable int count; }; void implode(const std::vector<std::string>& elems, char delim, std::string& s) { std::for_each(elems.begin(), elems.end(), appender(delim, s, elems.size())); }
只需添加! String s =“”;
for (int i = 0; i < doc.size(); i++) //doc is the vector s += doc[i];
首先,这里需要一个stream类ostringstream
来进行多次连接,并节省了过多的内存分配的底层麻烦。
码:
string join(const vector<string>& vec, const char* delim) { ostringstream oss; if(!string_vector.empty()) { copy(string_vector.begin(),string_vector.end() - 1, ostream_iterator<string>(oss, delim.c_str())); } return oss.str(); } vector<string> string_vector {"1", "2"}; string delim("->"); string joined_string = join(); // get "1->2"
说明:
思考时,把oss
这里当作std::cout
当我们想写:
std::cout << string_vector[0] << "->" << string_vector[1] << "->"
,
我们可以使用下面的STL类作为帮助:
ostream_iterator
在每次使用<<
时自动附加一个带有分隔符的包装输出stream。
例如,
ostream my_cout = ostream_iterator<string>(std::cout, "->")
将std:cout
包装为my_cout
所以每次你my_cout << "string_vector[0]"
,
它意味着std::cout << "string_vector[0]" << "->"
至于copy(vector.begin(), vector.end(), std::out);
它意味着std::cout << vector[0] << vector[1] (...) << vector[end]
这是我使用的,简单而灵活
string joinList(vector<string> arr, string delimiter) { if (arr.empty()) return ""; string str; for (auto i : arr) str += i + delimiter; str = str.substr(0, str.size() - delimiter.size()); return str; }
使用:
string a = joinList({ "a", "bbb", "c" }, "!@#");
输出:
a!@#bbb!@#c