如何从std :: vector <string>构造一个std :: string?

Wil*_*mKF 24 c++ stl string-concatenation stringstream stdstring

我想建立std::string一个std::vector<std::string>.

我可以使用std::stringsteam,但想象有一个更短的方式:

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();
}
Run Code Online (Sandbox Code Playgroud)

我怎么可能这样做?

Okt*_*ist 60

C++ 03

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

C++ 11 (MSVC 2010子集)

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

C++ 11

std::string s;
for (const auto &piece : v) s += piece;
return s;
Run Code Online (Sandbox Code Playgroud)

不要std::accumulate用于字符串连接,它是经典的Schlemiel the Painter的算法,甚至比strcat在C中使用的通常示例更糟糕.在没有C++ 11移动语义的情况下,它会为向量的每个元素产生两个不必要的累加器副本.即使使用移动语义,它仍然会为每个元素生成一个不必要的累加器副本.

以上三个例子是O(n).

std::accumulate字符串是O(n²).

您可以std::accumulate通过提供自定义仿函数为字符串生成O(n):

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

注意,s必须是对非const的引用,lambda返回类型必须是引用(因此decltype(auto)),而body必须使用 +=not +.

C++ 20

在当前预期成为C++ 20的草案中,定义std::accumulate已被改变std::move在附加到累加器时使用,因此从C++ 20开始,对于字符串accumulate将是O(n),并且可以使用作为单线:

std::string s = std::accumulate(v.begin(), v.end(), std::string{});
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢 FP 方式,但现在看起来有点奇怪。真的很期待 c++20 来澄清一切! (3认同)

And*_*owl 35

您可以使用std::accumulate()<numeric>头中的标准函数(它的工作原理是因为operator +strings 定义了重载,它返回了两个参数的串联):

#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!"
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用更高效,更小的for周期:

#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!"
}
Run Code Online (Sandbox Code Playgroud)

  • 可爱,但不小心.它将为每个操作分配一个新字符串,因为它使用`operator +`并生成一个新字符串,而不是`operator + =`来修改现有字符串. (8认同)

bst*_*our 8

为什么不使用operator +将它们添加到一起?

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

默认情况下,std :: accumulate使用std :: plus,并且在C++中添加两个字符串是连接,因为运算符+为std :: string重载.

  • 我可能也会,但这是原始问题中使用的名称. (2认同)

Ali*_*Ali 7

我个人的选择是基于范围的for循环,就像Oktalist的回答一样.

Boost还提供了一个很好的解决方案:

#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;
}
Run Code Online (Sandbox Code Playgroud)

这打印:

第一秒

在任何情况下,我发现该std::accumulate()方法滥用该算法(无论复杂性问题如何).


NoS*_*tAl 5

Google Abseil 具有absl::StrJoin 函数可以满足您的需要。

来自他们的头文件的示例。请注意,分隔符也可以是""

//   std::vector<std::string> v = {"foo", "bar", "baz"};
//   std::string s = absl::StrJoin(v, "-");
//   EXPECT_EQ("foo-bar-baz", s);
Run Code Online (Sandbox Code Playgroud)