Подтвердить что ты не робот

Как построить std::string из std::vector <string>?

Я хотел бы построить 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();
}

Как еще я могу это сделать?

4b9b3361

Ответ 1

Вы можете использовать стандартную функцию std::accumulate() из заголовка <numeric> (она работает, потому что перегрузка operator + определена для string, который возвращает конкатенацию двух своих аргументов):

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

Ответ 2

С++ 03

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

С++ 11 (подмножество MSVC 2010)

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

С++ 11

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

Не используйте std::accumulate для конкатенации строк, это классический алгоритм Шлемеля-Пейнтера, даже хуже, чем обычный пример, использующий strcat в 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; });

Обратите внимание, что s должен быть ссылкой на неконстантный, лямбда-тип возвращаемого значения должен быть ссылкой (следовательно, decltype(auto)), и тело должно использовать += не +.

С++ 20

В текущем проекте того, что, как ожидается, станет С++ 20, определение std::accumulate было изменено для использования std::move при добавлении к аккумулятору, поэтому начиная с С++ 20 и далее, accumulate будет равно O ( n) для строк, и может использоваться как однострочный:

std::string s = std::accumulate(v.begin(), v.end(), std::string{});

Ответ 3

Почему бы просто не использовать оператор + для их добавления?

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

std:: accumulate использует std:: plus под капотом по умолчанию, и добавление двух строк является конкатенацией в С++, поскольку оператор + перегружен для std::string.

Ответ 4

Моим личным выбором был бы цикл, основанный на диапазоне, как в ответ 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::accumulate() неправильным использованием этого алгоритма (независимо от сложности).

Ответ 5

Немного поздно вечеринке, но мне понравилось, что мы можем использовать списки инициализаторов:

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"})

Ответ 6

Google Abseil имеет функцию absl:: StrJoin, которая делает то, что вам нужно.

Пример из файла header. Обратите внимание, что разделитель также может быть ""

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

Ответ 7

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

Ответ 8

Если не требуется завершающих пробелов, используйте accumulate определенное в <numeric> с лямбдой нестандартного соединения.

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