How to deal with last comma, when making comma sep

2019-02-01 05:18发布

Possible Duplicates:
Don't print space after last number
Printing lists with commas C++

#include <vector>
#include <iostream>
#include <sstream>
#include <boost/foreach.hpp>
using namespace std;

int main()
{
   vector<int> VecInts;

   VecInts.push_back(1);
   VecInts.push_back(2);
   VecInts.push_back(3);
   VecInts.push_back(4);
   VecInts.push_back(5);

   stringstream ss;
   BOOST_FOREACH(int i, VecInts)
   {
      ss << i << ",";
   }

   cout << ss.str();

   return 0;
}

This prints out: 1,2,3,4,5, However I want: 1,2,3,4,5

How can I achieve that in an elegant way?

I see there is some confusion about what I mean with "elegant": E.g. no slowing down "if-clause" in my loop. Imagine 100.000 entries in the vector! If that is all you have to offer, I'd rather remove the last comma after I have gone through the loop.

10条回答
闹够了就滚
2楼-- · 2019-02-01 05:56

Using Karma from Boost Spirit - has a reputation for being fast.

#include <iostream>
#include <vector>
#include <boost/spirit/include/karma.hpp>

int main()
{
  std::vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);

  using namespace boost::spirit::karma;
  std::cout << format(int_ % ',', v) << std::endl;
}
查看更多
beautiful°
3楼-- · 2019-02-01 06:00

How about this:

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

int main()
{
   std::vector<int> v;

   v.push_back(1);
   v.push_back(2);
   v.push_back(3);
   v.push_back(4);
   v.push_back(5);

   std::ostringstream ss;

   std::copy(v.begin(), v.end() - 1, std::ostream_iterator<int>(ss, ", "));
   ss << v.back();

   std::cout << ss.str() << "\n";
}

No need to add extra variables and doesn't even depend on boost! Actually, in addition to the "no additional variable in the loop" requirement, one could say that there is not even a loop :)

查看更多
霸刀☆藐视天下
4楼-- · 2019-02-01 06:02

cout << ss.str()<<"\b" <<" ";

You can add the "\b" backspace

This will overwrite the extra "," .

for Example :

int main()
{
    cout<<"Hi";
    cout<<'\b';  //Cursor moves 1 position backwards
    cout<<" ";   //Overwrites letter 'i' with space
}

So the output would be

H
查看更多
Explosion°爆炸
5楼-- · 2019-02-01 06:06

Try:

if (ss.tellp ())
{
   ss << ",";
}
ss << i;

Alternatively, if the "if" is making you worried:

char *comma = "";
BOOST_FOREACH(int i, VecInts)
{
   ss << comma << i;
   comma = ",";
}
查看更多
霸刀☆藐视天下
6楼-- · 2019-02-01 06:07

Well, if you format into a stringstream anyway, you can just trim the resulting string by one character:

cout << ss.str().substr(0, ss.str().size() - 1);

If the string is empty, than the second argument says -1, which means everything and does not crash and if the string is non-empty, it always ends with a comma.

But if you write to an output stream directly, I never found anything better than the first flag.

That is unless you want to use join from boost.string algo.

查看更多
The star\"
7楼-- · 2019-02-01 06:12

Personally, I like a solution that does not cause potential memory allocations (because the string grows larger than needed). An extra-if within the loop body should be tractable thanks to branch target buffering, but I would do so:

#include <vector>
#include <iostream>

int main () {
    using std::cout;
    typedef std::vector<int>::iterator iterator;

    std::vector<int> ints;    
    ints.push_back(5);
    ints.push_back(1);
    ints.push_back(4);
    ints.push_back(2);
    ints.push_back(3);


    if (!ints.empty()) {
        iterator        it = ints.begin();
        const iterator end = ints.end();

        cout << *it;
        for (++it; it!=end; ++it) {
            cout << ", " << *it;
        }
        cout << std::endl;
    }
}

Alternatively, BYORA (bring your own re-usable algorithm):

// Follow the signature of std::getline. Allows us to stay completely
// type agnostic.
template <typename Stream, typename Iter, typename Infix>
inline Stream& infix (Stream &os, Iter from, Iter to, Infix infix_) {
    if (from == to) return os;
    os << *from;
    for (++from; from!=to; ++from) {
        os << infix_ << *from;
    }
    return os;
}

template <typename Stream, typename Iter>
inline Stream& comma_seperated (Stream &os, Iter from, Iter to) {
    return infix (os, from, to, ", ");
}

so that

...
comma_seperated(cout, ints.begin(), ints.end()) << std::endl;

infix(cout, ints.begin(), ints.end(), "-") << std::endl;
infix(cout, ints.begin(), ints.end(), "> <") << std::endl;
...

output:

5, 1, 4, 2, 3
5-1-4-2-3
5> <1> <4> <2> <3

The neat thing is it works for every output stream, any container that has forward iterators, with any infix, and with any infix type (interesting e.g. when you use wide strings).

查看更多
登录 后发表回答