问题
I\'m looking for the most elegant way to implode a vector of strings into a string. Below is the solution I\'m using now:
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);
}
Is there any others out there?
回答1:
Use boost::algorithm::join(..)
:
#include <boost/algorithm/string/join.hpp>
...
std::string joinedString = boost::algorithm::join(elems, delim);
See also this question.
回答2:
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));
(include <string>
, <vector>
, <sstream>
and <iterator>
)
If you want to have a clean end (no trailing delimiter) have a look here
回答3:
You should use std::ostringstream
rather than std::string
to build the output (then you can call its str()
method at the end to get a string, so your interface need not change, only the temporary s
).
From there, you could change to using std::ostream_iterator
, like so:
copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim));
But this has two problems:
delim
now needs to be aconst char*
, rather than a singlechar
. No big deal.std::ostream_iterator
writes the delimiter after every single element, including the last. So you'd either need to erase the last one at the end, or write your own version of the iterator which doesn't have this annoyance. It'd be worth doing the latter if you have a lot of code that needs things like this; otherwise the whole mess might be best avoided (i.e. useostringstream
but notostream_iterator
).
回答4:
Because I love one-liners (they are very useful for all kinds of weird stuff, as you'll see at the end), here's a solution using std::accumulate and 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;
} )
I find this syntax useful with stream operator, where I don't want to have all kinds of weird logic out of scope from the stream operation, just to do a simple string join. Consider for example this return statement from method that formats a string using stream operators (using 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();
回答5:
string join(const vector<string>& vec, const char* delim)
{
stringstream res;
copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim));
return res.str();
}
回答6:
A version that uses 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:
Especially with bigger collections, you want to avoid having to check if youre still adding the first element or not to ensure no trailing separator...
So for the empty or single-element list, there is no iteration at all.
Empty ranges are trivial: return "".
Single element or multi-element can be handled perfectly by 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;
});
};
Running sample (require C++14): http://cpp.sh/8uspd
回答8:
Here is another one that doesn't add the delimiter after the last element:
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 "";
回答9:
what about simple stupid solution?
std::string String::join(const std::vector<std::string> &lst, const std::string &delim)
{
std::string ret;
for(const auto &s : lst) {
if(!ret.empty())
ret += delim;
ret += s;
}
return ret;
}
回答10:
Using part of this answer to another question gives you a joined this, based on a separator without a trailing comma,
Usage:
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
Code:
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();
}
}
回答11:
I like to use this one-liner accumulate (no trailing delimiter):
std::accumulate(
std::next(elems.begin()),
elems.end(),
elems[0],
[](std::string a, std::string b) {
return a + delimiter + b;
}
);
回答12:
Slightly long solution, but doesn't use std::ostringstream
, and doesn't require a hack to remove the last delimiter.
http://www.ideone.com/hW1M9
And the code:
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()));
}
回答13:
just add !! String s = "";
for (int i = 0; i < doc.size(); i++) //doc is the vector
s += doc[i];
回答14:
Here's what I use, simple and flexible
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;
}
using:
string a = joinList({ "a", "bbb", "c" }, "!@#");
output:
a!@#bbb!@#c
回答15:
Firstly, a stream class ostringstream
is needed here to do concatenation for many times and save the underlying trouble of excessive memory allocation.
Code:
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"
Explanation:
when thinking, treat oss
here as std::cout
when we want to write:
std::cout << string_vector[0] << "->" << string_vector[1] << "->"
,
we can use the following STL classes as help:
ostream_iterator
returns an wrapped output stream with delimiters automatically appended each time you use <<
.
for instance,
ostream my_cout = ostream_iterator<string>(std::cout, "->")
wraps std:cout
as my_cout
so each time you my_cout << "string_vector[0]"
,
it means std::cout << "string_vector[0]" << "->"
As for
copy(vector.begin(), vector.end(), std::out);
it means
std::cout << vector[0] << vector[1] (...) << vector[end]
回答16:
try this, but using vector instead of 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()+ "]";
}
来源:https://stackoverflow.com/questions/5689003/how-to-implode-a-vector-of-strings-into-a-string-the-elegant-way