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

c++ stl string-concatenation stringstream stdstring

35352 просмотра

8 ответа

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

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

Автор: WilliamKF Источник Размещён: 17.06.2019 08:37

Ответы (8)


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

В качестве альтернативы вы можете использовать более эффективный маленький 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!"
}
Автор: Andy Prowl Размещён: 11.03.2013 07:43

49 плюса

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

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

Не используйте std::accumulateдля конкатенации строк , это классический алгоритм Шлемеля-Пейнтера , даже хуже, чем обычный пример, использующийся strcatв C. Без C ++ 11 семантики перемещения, он влечет за собой две ненужные копии аккумулятора для каждого элемента вектора. Даже с семантикой перемещения, он все еще содержит одну ненужную копию аккумулятора для каждого элемента.

Три приведенных выше примера O (n) .

std::accumulateэто O (N²) для строк.

Вы можете сделать std::accumulateO (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должна быть ссылка на non-const, лямбда-тип возвращаемого значения должен быть ссылкой (следовательно 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{});
Автор: Oktalist Размещён: 09.09.2013 05:30

8 плюса

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

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

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

Автор: bstamour Размещён: 11.03.2013 07:43

6 плюса

Мой личный выбор будет основан на цикле for, как в ответе Окталиста .

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()подход неправильным использованием этого алгоритма (независимо от сложности вопросов).

Автор: Ali Размещён: 17.05.2014 05:44

2 плюса

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

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"})
Автор: Benjamin K. Размещён: 23.07.2015 11:18

2 плюса

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

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

//   std::vector<std::string> v = {"foo", "bar", "baz"};
//   std::string s = absl::StrJoin(v, "-");
//   EXPECT_EQ("foo-bar-baz", s);
Автор: NoSenseEtAl Размещён: 13.12.2017 03:04

0 плюса

С 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();
}
Автор: PSIAlt Размещён: 11.03.2013 08:22

0 плюса

Если не требуется использовать пробелы в конце, используйте 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;
}

Выход:

fee fi foe fum
Автор: user9494810 Размещён: 17.06.2019 01:24
Вопросы из категории :
32x32