How to build std :: string from std :: vector <string>?

I would like to build std::string from std::vector<std::string> .

I could use std::stringsteam , but imagine a shorter path exists:

 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(); } 

How else can I do this?

+19
c ++ stdstring string-concatenation stl stringstream
Mar 11 '13 at 19:39
source share
8 answers

You can use the standard function std::accumulate() from the <numeric> header (it works because operator + overloading is defined for string , which returns the concatenation of its two arguments):

 #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!" } 

Alternatively, you can use a more efficient small for loop:

 #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!" } 
+35
Mar 11 '13 at 19:43
source share
β€” -

C ++ 03

 std::string s; for (std::vector<std::string>::const_iterator i = v.begin(); i != v.end(); ++i) s += *i; return s; 

C ++ 11 (a subset of MSVC 2010)

 std::string s; std::for_each(v.begin(), v.end(), [&](const std::string &piece){ s += piece; }); return s; 

C ++ 11

 std::string s; for (const auto &piece : v) s += piece; return s; 

Do not use std::accumulate to concatenate strings , this is the classic Schlemel-PΓ€nter algorithm , even worse than the usual example using strcat in C. Without C ++ 11 movement semantics, it causes two unnecessary battery copies for each element of the vector. Even with the semantics of movement, it still contains one unnecessary copy of the battery for each cell.

The three examples above are O (n) .

std::accumulate - O (nΒ²) for strings.

You can do std::accumulate O (n) for strings by providing a custom functor:

 std::string s = std::accumulate(v.begin(), v.end(), std::string{}, [](std::string &s, const std::string &piece) -> decltype(auto) { return s += piece; }); 

Note that s must be a reference to non-constant, the lambda type of the return value must be a reference (hence decltype(auto) ), and the body must use += not + .

C ++ 20

In the current project of what is expected to become C ++ 20, the definition of std::accumulate been changed to use std::move when added to the battery, so starting from C ++ 20 onwards, accumulate will be O (n ) for strings, and can be used as a single line:

 std::string s = std::accumulate(v.begin(), v.end(), std::string{}); 
+49
Sep 09 '13 at 17:30
source share

Why not just use the + operator to add them?

 std::string string_from_vector(const std::vector<std::string> &pieces) { return std::accumulate(pieces.begin(), pieces.end(), std::string("")); } 

std :: accumulate uses std :: plus under the hood by default, and adding two lines is a concatenation in C ++, since the + operator is overloaded for std :: string.

+8
Mar 11 '13 at 19:43
source share

My personal choice would be a range-based cycle, as in Oktalist's answer .

Boost also offers a good solution:

 #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; } 

Fingerprints:

first second

In any case, I find the std::accumulate() approach to be misused by this algorithm (regardless of complexity).

+6
May 17 '14 at 17:44
source share

A little late to the party, but I liked that we could use initializer lists:

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

Then you can just call (Python style):

 join({"Hello", "World", "1"}) 
+2
Jul 23 '15 at 11:18
source share

Google Abseil has an absl :: StrJoin function that does what you need.

An example from the header file. Please note that the delimiter may also be ""

 // std::vector<std::string> v = {"foo", "bar", "baz"}; // std::string s = absl::StrJoin(v, "-"); // EXPECT_EQ("foo-bar-baz", s); 
+2
Dec 13 '17 at 3:04 on
source share

With C ++ 11, the stringstream method is not too scary:

 #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(); } 
0
Mar 11 '13 at 20:22
source share

If no trailing spaces are required, use the accumulate defined in <numeric> with a non-standard connection 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; } 
0
Jun 17 '19 at 1:24
source share



All Articles