Преобразовать вектор <int>в строку - PullRequest
83 голосов
/ 16 сентября 2009

У меня есть контейнер vector<int> с целыми числами (например, {1,2,3,4}), и я хотел бы преобразовать его в строку вида

"1,2,3,4"

Какой самый чистый способ сделать это в C ++? В Python я бы так и сделал:

>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'

Ответы [ 19 ]

86 голосов
/ 16 сентября 2009

Определенно не такой элегантный, как Python, но нет ничего более элегантного, чем Python в C ++.

Вы можете использовать stringstream ...

std::stringstream ss;
for(size_t i = 0; i < v.size(); ++i)
{
  if(i != 0)
    ss << ",";
  ss << v[i];
}
std::string s = ss.str();

Вы также можете использовать std::for_each.

43 голосов
/ 16 сентября 2009

Используя std :: for_each и lambda, вы можете сделать что-то интересное.

#include <iostream>
#include <sstream>

int main()
{
     int  array[] = {1,2,3,4};
     std::for_each(std::begin(array), std::end(array),
                   [&std::cout, sep=' '](int x) mutable {
                       out << sep << x; sep=',';
                   });
}

См. этот вопрос для небольшого класса, который я написал. Это не будет печатать запятую. Также, если мы предположим, что C ++ 14 будет продолжать давать нам основанные на диапазоне эквиваленты алгоритмов, подобных этому:

namespace std {
   // I am assuming something like this in the C++14 standard
   // I have no idea if this is correct but it should be trivial to write if it  does not appear.
   template<typename C, typename I>
   void copy(C const& container, I outputIter) {copy(begin(container), end(container), outputIter);}
}
using POI = PrefexOutputIterator;   
int main()
{
     int  array[] = {1,2,3,4};
     std::copy(array, POI(std::cout, ","));
  // ",".join(map(str,array))               // closer
}
19 голосов
/ 31 мая 2015

Вы можете использовать std :: накопить. Рассмотрим следующий пример

if (v.empty() 
    return std::string();
std::string s = std::accumulate(v.begin()+1, v.end(), std::to_string(v[0]),
                     [](const std::string& a, int b){
                           return a + ',' + std::to_string(b);
                     });
17 голосов
/ 16 сентября 2009

Другой альтернативой является использование std::copy и класса ostream_iterator:

#include <iterator>  // ostream_iterator
#include <sstream>   // ostringstream
#include <algorithm> // copy

std::ostringstream stream;
std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream));
std::string s=stream.str();
s.erase(s.length()-1);

Также не так хорош, как Python. Для этого я создал функцию join:

template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
  T result;
  for (A it=begin;
       it!=end;
       it++)
  {
    if (!result.empty())
      result.append(t);
    result.append(*it);
  }
  return result;
}

Затем использовал это так:

std::string s=join(array.begin(), array.end(), std::string(","));

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

std::string s=join(array.rbegin(), array.rend(), std::string(","));

В идеале, я бы хотел создать шаблон, чтобы он мог выводить тип символа и использовать строковые потоки, но я пока не мог понять это.

12 голосов
/ 06 октября 2015

С Boost и C ++ 11 это может быть достигнуто следующим образом:

auto array = {1,2,3,4};
join(array | transformed(tostr), ",");

Ну, почти. Вот полный пример:

#include <array>
#include <iostream>

#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>

int main() {
    using boost::algorithm::join;
    using boost::adaptors::transformed;
    auto tostr = static_cast<std::string(*)(int)>(std::to_string);

    auto array = {1,2,3,4};
    std::cout << join(array | transformed(tostr), ",") << std::endl;

    return 0;
}

Кредит Преторианец .

Вы можете обработать любой тип значения следующим образом:

template<class Container>
std::string join(Container const & container, std::string delimiter) {
  using boost::algorithm::join;
  using boost::adaptors::transformed;
  using value_type = typename Container::value_type;

  auto tostr = static_cast<std::string(*)(value_type)>(std::to_string);
  return join(container | transformed(tostr), delimiter);
};
11 голосов
/ 16 сентября 2009

Это всего лишь попытка разгадать загадку, заданную замечанием 1800 ИНФОРМАЦИИ о его втором решении, не имеющем универсальности, а не попыткой ответить на вопрос:

template <class Str, class It>
Str join(It begin, const It end, const Str &sep)
{
  typedef typename Str::value_type     char_type;
  typedef typename Str::traits_type    traits_type;
  typedef typename Str::allocator_type allocator_type;
  typedef std::basic_ostringstream<char_type,traits_type,allocator_type>
                                       ostringstream_type;
  ostringstream_type result;

  if(begin!=end)
    result << *begin++;
  while(begin!=end) {
    result << sep;
    result << *begin++;
  }
  return result.str();
}

Работает на моей машине (ТМ).

7 голосов
/ 01 мая 2014

Много шаблонов / идей. Мой не такой общий или эффективный, но у меня просто была та же проблема, и я хотел добавить это в микс как что-то короткое и сладкое. Он выигрывает на самом коротком количестве линий ...:)

std::stringstream joinedValues;
for (auto value: array)
{
    joinedValues << value << ",";
}
//Strip off the trailing comma
std::string result = joinedValues.str().substr(0,joinedValues.str().size()-1);
4 голосов
/ 22 июня 2015

Если вы хотите сделать std::cout << join(myVector, ",") << std::endl;, вы можете сделать что-то вроде:

template <typename C, typename T> class MyJoiner
{
    C &c;
    T &s;
    MyJoiner(C &&container, T&& sep) : c(std::forward<C>(container)), s(std::forward<T>(sep)) {}
public:
    template<typename C, typename T> friend std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj);
    template<typename C, typename T> friend MyJoiner<C, T> join(C &&container, T&& sep);
};

template<typename C, typename T> std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj)
{
    auto i = mj.c.begin();
    if (i != mj.c.end())
    {
        o << *i++;
        while (i != mj.c.end())
        {
            o << mj.s << *i++;
        }
    }

    return o;
}

template<typename C, typename T> MyJoiner<C, T> join(C &&container, T&& sep)
{
    return MyJoiner<C, T>(std::forward<C>(container), std::forward<T>(sep));
}

Обратите внимание, что это решение выполняет объединение непосредственно в выходной поток, а не создает вторичный буфер, и будет работать с любыми типами, у которых есть оператор << в ostream. </p>

Это также работает, когда boost::algorithm::join() терпит неудачу, когда у вас есть vector<char*> вместо vector<string>.

2 голосов
/ 23 января 2019
string s;
for (auto i : v)
    s += (s.empty() ? "" : ",") + to_string(i);
2 голосов
/ 16 сентября 2009

Мне нравится ответ 1800 года. Однако я бы переместил первую итерацию из цикла, так как в результате оператор if изменяется только один раз после первой итерации

template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
  T result;
  A it = begin;
  if (it != end) 
  {
   result.append(*it);
   ++it;
  }

  for( ;
       it!=end;
       ++it)
  {
    result.append(t);
    result.append(*it);
  }
  return result;
}

Это, конечно, можно сократить до меньшего количества утверждений, если вам нравится:

template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
  T result;
  A it = begin;
  if (it != end) 
   result.append(*it++);

  for( ; it!=end; ++it)
   result.append(t).append(*it);
  return result;
}
...