我正在寻找将字符串向量内嵌到字符串中的最优雅方式。以下是我现在使用的解决方案:
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);
}
那里还有其他人吗?
答案 0 :(得分:116)
使用boost::algorithm::join(..)
:
#include <boost/algorithm/string/join.hpp>
...
std::string joinedString = boost::algorithm::join(elems, delim);
答案 1 :(得分:24)
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>
)
<强> If you want to have a clean end (no trailing delimiter) have a look here 强>
答案 2 :(得分:20)
您应该使用std::ostringstream
而不是std::string
来构建输出(然后您可以在最后调用其str()
方法来获取字符串,因此您的界面无需更改,仅临时s
)。
从那里,您可以更改为使用std::ostream_iterator
,如下所示:
copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim));
但这有两个问题:
delim
现在必须是const char*
,而不是单char
。没什么大不了的。std::ostream_iterator
在每个元素之后写入分隔符,包括最后一个元素。因此,你需要在最后删除最后一个,或者编写你自己的迭代器版本,这个版本没有这个烦恼。如果你有很多代码需要这样的东西,那么值得做后者;否则可能最好避免整个混乱(即使用ostringstream
而不是ostream_iterator
)。答案 3 :(得分:10)
因为我喜欢单行(它们对各种奇怪的东西非常有用,你最后会看到),这里有一个使用std :: accumulate和C ++ 11 lambda的解决方案:
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运算符非常有用,我不希望在流操作的范围之外有各种奇怪的逻辑,只是做一个简单的字符串连接。例如,考虑使用流操作符格式化字符串的方法的return语句(使用std;):
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();
答案 4 :(得分:8)
string join(const vector<string>& vec, const char* delim)
{
stringstream res;
copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim));
return res.str();
}
答案 5 :(得分:6)
有了 fmt,你就可以做到。
#include <fmt/format.h>
auto s = fmt::format("{}",fmt::join(elems,delim));
但我不知道 join 是否会使其成为 std::format。
答案 6 :(得分:3)
使用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";
}
答案 7 :(得分:3)
特别是对于更大的集合,您希望避免检查是否仍然添加第一个元素以确保没有尾随分隔符...
因此对于空元素或单元素列表,根本没有迭代。
空的范围是微不足道的:返回&#34;&#34;。
单个元素或多元素可以通过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
答案 8 :(得分:3)
简单的愚蠢解决方案呢?
set -x
a="My name is Vikas"
b="${a} Singh"
set +x
答案 9 :(得分:2)
这是另一个在最后一个元素之后不添加分隔符的方法:
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 "";
答案 10 :(得分:2)
我喜欢使用这种单线累积(没有尾随定界符):
std::accumulate(
std::next(elems.begin()),
elems.end(),
elems[0],
[](std::string a, std::string b) {
return a + delimiter + b;
}
);
答案 11 :(得分:2)
将此answer的一部分用于另一个问题,根据没有尾随逗号的分隔符,您可以加入此
用法:
std::vector<std::string> input_str = std::vector<std::string>({"a", "b", "c"});
std::string result = string_join(input_str, ",");
printf("%s", result.c_str());
/// a,b,c
代码:
std::string string_join(const std::vector<std::string>& elements, const char* const separator)
{
switch (elements.size())
{
case 0:
return "";
case 1:
return elements[0];
default:
std::ostringstream os;
std::copy(elements.begin(), elements.end() - 1, std::ostream_iterator<std::string>(os, separator));
os << *elements.rbegin();
return os.str();
}
}
答案 12 :(得分:1)
另一个简单而好的解决方案是使用 ranges v3。当前版本为 C++14 或更高版本,但也有 C++11 或更高版本的旧版本。不幸的是,C++20 范围没有 intersperse
函数。
这种方法的好处是:
功能细分(Reference):
accumulate
= 类似于 std::accumulate
但参数是一个范围和初始值。有一个可选的第三个参数,即运算符函数。filter
= 像 std::filter
一样,过滤掉不符合谓词的元素。intersperse
= 关键功能!在范围输入元素之间散布分隔符。#include <iostream>
#include <string>
#include <vector>
#include <range/v3/numeric/accumulate.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/intersperse.hpp>
int main()
{
using namespace ranges;
// Can be any std container
std::vector<std::string> a{ "Hello", "", "World", "is", "", "a", "program" };
std::string delimiter{", "};
std::string finalString =
accumulate(a | views::filter([](std::string s){return !s.empty();})
| views::intersperse(delimiter)
, std::string());
std::cout << finalString << std::endl; // Hello, World, is, a, program
}
答案 13 :(得分:1)
首先,这里需要一个流类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
时, <<
都会返回一个自动附加分隔符的包装输出流。
例如,
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]
答案 14 :(得分:1)
这是我使用的,简单而灵活的
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
答案 15 :(得分:1)
添加!! String s =“”;
for (int i = 0; i < doc.size(); i++) //doc is the vector
s += doc[i];
答案 16 :(得分:1)
稍长的解决方案,但不使用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()));
}
答案 17 :(得分:0)
试试这个,但使用vector而不是list
template <class T>
std::string listToString(std::list<T> l){
std::stringstream ss;
for(std::list<int>::iterator it = l.begin(); it!=l.end(); ++it){
ss << *it;
if(std::distance(it,l.end())>1)
ss << ", ";
}
return "[" + ss.str()+ "]";
}
答案 18 :(得分:0)
使用三元运算符?:
的可能解决方案。
std::string join(const std::vector<std::string> & v, const std::string & delimiter = ", ") {
std::string result;
for (size_t i = 0; i < v.size(); ++i) {
result += (i ? delimiter : "") + v[i];
}
return result;
}
join({"2", "4", "5"})
会给您2, 4, 5
。
答案 19 :(得分:0)
这个可以用boost解决
#include <boost/range/adaptor/filtered.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/algorithm.hpp>
std::vector<std::string> win {"Stack", "", "Overflow"};
const std::string Delimitor{","};
const std::string combined_string =
boost::algorithm::join(win |
boost::adaptors::filtered([](const auto &x) {
return x.size() != 0;
}), Delimitor);
Output:
combined_string: "Stack,Overflow"