views:

477

answers:

5

I have a vector<int> container that has integers (e.g. {1,2,3,4}) and I would like to convert to a string of the form

"1,2,3,4"

What is the cleanest way to do that in C++? In Python this is how I would do it:

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

Definitely not as eligant as Python, but nothing quite is as elegant as Python in C++.

You could use a 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();

You could also make use of std::for_each instead.

Brian R. Bondy
I think you mean array.size() not v.size(), no?
Mark E
ya whatever the vector is called.
Brian R. Bondy
Thanks! Now I need to convert ss to char* type (for use with a C function). Any suggestions on how to do that?
celil
Use s.c_str()
Ralph
That should be `std::string s = ss.str()`. If you want a `const char*`, use `s.c_str()`. (Note that, while syntactically correct, `ss.str().c_str()` will give you a `const char*` that points to a temporary which will will cease to exist at the end of the full expression. That hurts.)
sbi
Thanks sbi: Changed ss.string() to ss.str(). boost path_t has .string() so that's where that came from :)
Brian R. Bondy
+7  A: 

Using std::copy and std::ostream_iterator we can get something as elegant as python.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>


int main()
{
    int  x[] = {1,2,3,4};

    std::stringstream  s;
    std::copy(x,x+4,std::ostream_iterator<int>(s,","));

    std::cout << s.str() << "\n";
}
Martin York
I think this is not quite equivalent to Python's join - it will insert an extra "," at the end.
1800 INFORMATION
Not equivalent but just as elegant (in fact I think more so but that is just an opinion).
Martin York
Obviously elegance is subjective. So if you and two other people prefer longer, more repetitive code that doesn't work, then it's more elegant ;-p
Steve Jessop
Why doesn't it work?
jalf
You can ignore the final comma by using the string::substr member function. Assign the the substring from 0 to n-1 to your result variable.
Dan
+3  A: 

Another alternative is the use of std::copy and the ostream_iterator class:

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

Also not as nice as Python. For this purpose, I created a join function:

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

Then used it like this:

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

You might ask why I passed in the iterators. Well, actually I wanted to reverse the array, so I used it like this:

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

Ideally, I would like to template out to the point where it can infer the char type, and use string-streams, but I couldn't figure that out yet.

1800 INFORMATION
Since it would be too much for a comment, I have posted an answer (http://stackoverflow.com/questions/1430757/1432040#1432040) which attempts to solve the riddle given in your last sentence.
sbi
+3  A: 

This is just an attempt to solve the riddle given by 1800 INFORMATION's remark on his second solution lacking genericity, not an attempt to answer the question:

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

Works On My Machine(TM).

sbi
A: 

I like 1800's answer. However I would move the first iteration out of the loop as as the result of the if statement only changes once after the first iteration

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

This can of course be reduced down to fewer statements if you like:

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;
}
iain
You shouldn't use post-increment for unknown iterator types. That might be expensive. (Of course, when dealing with strings, that might not make that much of a difference. But once you learn the habit...)
sbi
Post increment is fine as long as you use the tempory value that is returned. eg "result.append(*it); ++it;" is almost always as expensive as "result.append(*it++);" the second has one extra copy of the iterator.
iain
Oops I just spotted the post increment in the for loop. copy and paste error. I have fixed the post.
iain
@Ian: When I taught C++, I hammered into my students to use `++i` except where they really needed `i++` because that was the only way they wouldn't forget this when it made a difference. (It was the same with me, BTW.) They had learned Java before, where all kinds of C-isms are en vogue and it took them a few months (1 lecture +lab work per week), but in the end most of them learned the habit to use pre-increment.
sbi
@sbi: agreed I always default to preincrement too, the rogue postincrement came from copying someone elses for loop and changing it. In my first reply I thought you were worried about "result.append(*it++)" and not the for loop. I was a little embarrassed to see the post increment in the loop.Some people seem to follow the advice of not using post increment too far and never use it or change it even when it is appropriate. However I know now you don't fall in to this category.
iain