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

How to construct a std::string from a std::vector<string>?

本文关键字:string std gt 一个 lt vector      更新时间:2023-10-16

我想从std::vector<std::string>构建一个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();
}

我还能怎么做呢?

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 (MSVC 2010子集)

std::string s;
std::for_each(v.begin(), v.end(), [&](const std::string &piece){ s += piece; });
return s;
<标题> c++ 11 h1> strong>不要使用std::accumulate进行字符串连接,这是一个经典的Schlemiel the Painter算法,甚至比在C中使用strcat的通常示例更糟糕。没有c++ 11的移动语义,它会为向量的每个元素产生两个不必要的累加器副本。即使使用move语义,它仍然会为每个元素产生一次不必要的累加器拷贝。

上面三个例子是O(n)

std::accumulate is O(n²) for string .

您可以通过提供a来使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; });

注意s必须是对非const (lambda返回类型)的引用必须是引用(因此decltype(auto)),并且主体必须使用+= 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{});

您可以使用<numeric>头文件中的std::accumulate()标准函数(它可以工作,因为为string定义了operator +的重载,它返回其两个参数的连接):

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

或者,您可以使用更有效的小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!"
}

我个人的选择是基于范围的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;
}
这个打印

:

第一,第二

为什么不直接使用运算符+将它们相加呢?

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

std::accumulate默认在底层使用std::plus,在c++中,添加两个字符串是串联的,因为std::string重载了+操作符。

Google Abseil有函数absl::StrJoin,做你需要的。

头文件中的例子。注意分隔符也可以是""

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

有点晚了,但我喜欢我们可以使用初始化器列表的事实:

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

然后你可以调用(Python风格):

join({"Hello", "World", "1"})

如果不需要尾随空格,则使用<numeric>中定义的accumulate和自定义连接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;
}
输出:

fee fi foe fum

在c++11中,stringstream的方式并不太可怕:

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