How to explode a string vector into a string (elegant way)

I am looking for the most elegant way to insert a row vector into a row. 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); } 

Are there others there?

+66
c ++ string stdstring stl implode
Apr 16 '11 at 19:29
source share
16 answers

This may not be obvious, but by adding lines to the implode , you do a lot of memory allocation and freeing up. A possible improvement is to make reserve() for s once, and then all your additions.

+5
Apr 16 '11 at 19:38
source share

Use boost::algorithm::join(..) :

 #include <boost/algorithm/string/join.hpp> ... std::string joinedString = boost::algorithm::join(elems, delim); 

See also this question .

+118
Jun 13 '11 at 17:52
source share
 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 limiter), look here

+24
Apr 16 2018-11-11T00:
source share

You should use std::ostringstream instead of std::string to assemble the output (then you can call its str() method at the end to get a string, so your interface should not change, but only a temporary s )).

From there, you can move on to using std::ostream_iterator , for example:

 copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim)); 

But this has two problems:

  • delim should now be const char* , not one char . No big deal.
  • std::ostream_iterator writes a separator after each individual element, including the last. Thus, you need to either delete the last one at the end, or write your own version of the iterator that does not have this annoyance. It would be helpful to do the latter if you have a lot of code that needs such things; otherwise, the whole mess could be avoided (i.e. use ostringstream , but not ostream_iterator ).
+21
Apr 16 '11 at 19:33
source share

Because I love single-line files (they are very useful for all kinds of strange things, as you will see at the end), here is 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 for the stream operator, where I do not want all kinds of weird logic outside the scope to be executed from the stream, just for simple concatenation of strings. Consider, for example, this return statement from a 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(); 
+11
Aug 28 2018-12-12T00:
source share
 string join(const vector<string>& vec, const char* delim) { stringstream res; copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim)); return res.str(); } 
+8
Aug 25 '13 at 8:52
source share

Version using 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"; } 
+4
Apr 18 '11 at 19:37
source share

Especially with large collections you want to avoid checking if you want to add the first element or not so that there is no trailing separator ...

So for an empty or singleton list there is no iteration at all.

Empty ranges are trivial: return "".

A single element or multi-element can be handled perfectly with 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; }); }; 

Sample launch ( C ++ 14 required ): http://cpp.sh/8uspd

+4
Sep 13 '16 at 10:16
source share

Here is another that does not add a separator 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 ""; 
+3
Sep 02 '14 at 23:18
source share

how about a 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; } 
+3
Jan 18 '18 at 12:57
source share

Using part of this answer to another question gives you a unified, comma-separated delimiter based,

Using:

 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 

The 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(); } } 
+3
Jun 18 '18 at 16:09
source share

A slightly long solution, but does not use std::ostringstream and does not require hacking to remove the last separator.

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())); } 
+1
Apr 16 2018-11-21T00:
source share

just add !! String s = "";

 for (int i = 0; i < doc.size(); i++) //doc is the vector s += doc[i]; 
+1
Dec 05 '13 at 11:25
source share

Here is 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; } 

via:

 string a = joinList({ "a", "bbb", "c" }, "!@#"); 

exit:

 a!@#bbb!@#c 
+1
Mar 29 '16 at
source share

First, this requires the ostringstream stream ostringstream to concatenate multiple times and preserve the main problem of excessive memory allocation.

the 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:

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 reference:

ostream_iterator returns a wrapped output stream with delimiters automatically added each time << used.

eg,

ostream my_cout = ostream_iterator<string>(std::cout, "->")

wraps std:cout as my_cout

so every time you my_cout << "string_vector[0]" ,

this means std::cout << "string_vector[0]" << "->"

As for copy(vector.begin(), vector.end(), std::out);

this means std::cout << vector[0] << vector[1] (...) << vector[end]

+1
Jun 22 '16 at 3:56 on
source share

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()+ "]"; } 
0
Feb 21 '18 at 14:29
source share



All Articles