Printing C ++ Comma Lists

I know how to do this in other languages, but not in C ++, which I have to use here.

I have a set of lines that I print in a list and they need a comma between each, but not a trailing comma. For example, in java, I would use a string constructor and just remove the comma from the end after I built my string. How to do it in C ++?

auto iter = keywords.begin(); for (iter; iter != keywords.end( ); iter++ ) { out << *iter << ", "; } out << endl; 

At first I tried to insert this block to do this (moving the comma here)

 if (iter++ != keywords.end()) out << ", "; iter--; 

I hate it when little things touch me.

EDIT: Thanks to everyone. That is why I post such things here. So many good answers and are decided differently. After the Java semester and assembly (different classes), the need to complete a C ++ project in 4 days threw me into a loop. I not only got my answer, I got the opportunity to think about different ways to solve such a problem. Tall.

+47
c ++ pretty-print
Aug 16 '10 at 20:18
source share
25 answers

Use infix_iterator:

 // infix_iterator.h // // Lifted from Jerry Coffin prefix_ostream_iterator #if !defined(INFIX_ITERATOR_H_) #define INFIX_ITERATOR_H_ #include <ostream> #include <iterator> template <class T, class charT=char, class traits=std::char_traits<charT> > class infix_ostream_iterator : public std::iterator<std::output_iterator_tag,void,void,void,void> { std::basic_ostream<charT,traits> *os; charT const* delimiter; bool first_elem; public: typedef charT char_type; typedef traits traits_type; typedef std::basic_ostream<charT,traits> ostream_type; infix_ostream_iterator(ostream_type& s) : os(&s),delimiter(0), first_elem(true) {} infix_ostream_iterator(ostream_type& s, charT const *d) : os(&s),delimiter(d), first_elem(true) {} infix_ostream_iterator<T,charT,traits>& operator=(T const &item) { // Here the only real change from ostream_iterator: // Normally, the '*os << item;' would come before the 'if'. if (!first_elem && delimiter != 0) *os << delimiter; *os << item; first_elem = false; return *this; } infix_ostream_iterator<T,charT,traits> &operator*() { return *this; } infix_ostream_iterator<T,charT,traits> &operator++() { return *this; } infix_ostream_iterator<T,charT,traits> &operator++(int) { return *this; } }; #endif 

Usage will be something like this:

 #include "infix_iterator.h" // ... std::copy(keywords.begin(), keywords.end(), infix_iterator(out, ",")); 
+51
Aug 16 '10 at 20:24
source share

In the C ++ 17 experimental prebuilt compiler, which will be available to you soon, you can use std::experimental::ostream_joiner :

 #include <algorithm> #include <experimental/iterator> #include <iostream> #include <iterator> int main() { int i[] = {1, 2, 3, 4, 5}; std::copy(std::begin(i), std::end(i), std::experimental::make_ostream_joiner(std::cout, ", ")); } 

Real-time examples using GCC 6.0 SVN and Clang 3.9 SVN

+26
Mar 14 '16 at 21:18
source share

Since everyone has decided to do this using while loops, I will give an example for loops.

 for (iter = keywords.begin(); iter != keywords.end(); iter++) { if (iter != keywords.begin()) cout << ", "; cout << *iter; } 
+25
Aug 16 '10 at 20:29
source share

One general approach is to print the first element before the cycle and execute the cycle only on the remaining elements, PRE-print a comma before each remaining element.

Alternatively, you can create your own stream that maintains the current state of the line (to the end) and puts the commas in the right place.

EDIT: You can also use the intermediate testing loop suggested by TED. It will be something like:

 if(!keywords.empty()) { auto iter = keywords.begin(); while(true) { out << *iter; ++iter; if(iter == keywords.end()) { break; } else { out << ", "; } } } 

At first I mentioned the method β€œprint the first element before the loop first”, because it keeps the loop body very simple, but either approach works fine.

+17
Aug 16 '10 at 20:22
source share

Assuming a dimly normal output stream, so writing an empty string to it really does nothing:

 const char *padding = ""; for (auto iter = keywords.begin(); iter != keywords.end(); ++iter) { out << padding << *iter; padding = ", " } 
+17
Aug 16 '10 at 23:05
source share

Something like that?

 while (iter != keywords.end()) { out << *iter; iter++; if (iter != keywords.end()) cout << ", "; } 
+7
Aug 16 '10 at 20:23
source share

There are many smart solutions, and too many that cripple the code besides the hope of salvation, preventing the compiler from doing its job.

The obvious solution is a special case of the first iteration:

 bool first = true; for (auto const& e: sequence) { if (first) { first = false; } else { out << ", "; } out << e; } 

This is a dead simple template that:

  • Does not distort the cycle: it is still obvious at first glance that each element will be repeated.
  • It allows more than just placing a separator or actually printing a list, since the else block and the body of the loop can contain arbitrary statements.

This may not be the most efficient code, but the potential performance loss of one well-predicted branch is likely to be overshadowed by the massive hippo, which is std::ostream::operator<< .

+6
Feb 14 '16 at 15:11
source share

My typical method for doing delimiters (in any language) is to use a loop with intermediate testing. C ++ Code:

 for (;;) { std::cout << *iter; if (++iter == keywords.end()) break; std::cout << ","; } 

(note: an additional if check is required before the loop if keywords can be empty)

Most of the other solutions shown ultimately complete the entire additional test at each iteration of the loop. You do I / O, so the time spent on this is not a big problem, but it offends my sensitivity.

+5
Aug 16 '10 at 20:49
source share

Try the following:

 typedef std::vector<std::string> Container; typedef Container::const_iterator CIter; Container data; // Now fill the container. // Now print the container. // The advantage of this technique is that ther is no extra test during the loop. // There is only one additional test !test.empty() done at the beginning. if (!data.empty()) { std::cout << data[0]; for(CIter loop = data.begin() + 1; loop != data.end(); ++loop) { std::cout << "," << *loop; } } 
+3
Aug 16 '10 at 20:25
source share

There is a small problem with the ++ operator that you are using.

You can try:

 if (++iter != keywords.end()) out << ", "; iter--; 

Thus, ++ will be evaluated before comparing the iterator with keywords.end() .

+2
Aug 16 '10 at 20:23
source share

In python we just write:

 print ", ".join(keywords) 

so why not:

 template<class S, class V> std::string join(const S& sep, const V& v) { std::ostringstream oss; if (!v.empty()) { typename V::const_iterator it = v.begin(); oss << *it++; for (typename V::const_iterator e = v.end(); it != e; ++it) oss << sep << *it; } return oss.str(); } 

and then just use it like:

 cout << join(", ", keywords) << endl; 

Unlike the python example, where " " is the string, and keywords is the iterability of the strings, here in this C ++ example the separator and keywords can be any stream, for example

 cout << join('\n', keywords) << endl; 
+2
Sep 17 '15 at 7:52
source share

to avoid placing the if inside the loop, I use this:

 vector<int> keywords = {1, 2, 3, 4, 5}; if (!keywords.empty()) { copy(keywords.begin(), std::prev(keywords.end()), std::ostream_iterator<int> (std::cout,", ")); std::cout << keywords.back(); } 

It depends on the type of the vector, int , but you can remove it with some help.

+2
Aug 23 '16 at 7:28
source share

The following should be done: -

  const std::vector<__int64>& a_setRequestId std::stringstream strStream; std::copy(a_setRequestId.begin(), a_setRequestId.end() -1, std::ostream_iterator<__int64>(strStream, ", ")); strStream << a_setRequestId.back(); 
+2
Dec 28 '16 at 22:33
source share

I suggest you just switch the first character with lambda.

 std::function<std::string()> f = [&]() {f = [](){ return ","; }; return ""; }; for (auto &k : keywords) std::cout << f() << k; 
+2
Aug 18 '17 at 14:18
source share

If the values ​​are std::string you can write it beautifully in declarative style with range-v3

 #include <range/v3/all.hpp> #include <vector> #include <iostream> #include <string> int main() { using namespace ranges; std::vector<std::string> const vv = { "a","b","c" }; auto joined = vv | view::join(','); std::cout << to_<std::string>(joined) << std::endl; } 

For other types that need to be converted to a string, you can simply add a conversion that calls to_string .

 #include <range/v3/all.hpp> #include <vector> #include <iostream> #include <string> int main() { using namespace ranges; std::vector<int> const vv = { 1,2,3 }; auto joined = vv | view::transform([](int x) {return std::to_string(x);}) | view::join(','); std::cout << to_<std::string>(joined) << std::endl; } 
+2
May 08 '18 at 20:30
source share

I think this should work

 while (iter != keywords.end( )) { out << *iter; iter++ ; if (iter != keywords.end( )) out << ", "; } 
+1
Aug 16 '10 at 20:23
source share

Maybe so.

 bool bFirst = true; for (auto curr = keywords.begin(); curr != keywords.end(); ++curr) { std::cout << (bFirst ? "" : ", ") << *curr; bFirst = false; } 
+1
Aug 16 '10 at 21:43
source share

I use a small helper class for this:

 class text_separator { public: text_separator(const char* sep) : sep(sep), needsep(false) {} // returns an empty string the first time it is called // returns the provided separator string every other time const char* operator()() { if (needsep) return sep; needsep = true; return ""; } void reset() { needsep = false; } private: const char* sep; bool needsep; }; 

To use it:

 text_separator sep(", "); for (int i = 0; i < 10; ++i) cout << sep() << i; 
+1
Jul 11 2018-11-11T00:
source share

Using boost:

 std::string add_str(""); const std::string sep(","); for_each(v.begin(), v.end(), add_str += boost::lambda::ret<std::string>(boost::lambda::_1 + sep)); 

and you get a string containing a comma separated vector.

EDIT: to remove the last comma just enter:

 add_str = add_str.substr(0, add_str.size()-1); 
+1
Jun 05 '12 at 11:33
source share

You can use the do loop, rewrite the loop condition for the first iteration, and use the && short circuit operator and the fact that the valid stream is true .

 auto iter = keywords.begin(); if ( ! keywords.empty() ) do { out << * iter; } while ( ++ iter != keywords.end() && out << ", " ); out << endl; 
0
Aug 16 '10 at 21:55
source share

I would go with something like this, an easy solution, and should work for all iterators.

 int maxele = maxele = v.size() - 1; for ( cur = v.begin() , i = 0; i < maxele ; ++i) { std::cout << *cur++ << " , "; } if ( maxele >= 0 ) { std::cout << *cur << std::endl; } 
0
Aug 16 '10 at 23:14
source share

Another possible solution to avoid if

 Char comma = '['; for (const auto& element : elements) { std::cout.put(comma) << element; comma = ','; } std::cout.put(']'); 

Depends on what you do in your cycle.

0
Jul 22 '13 at 16:15
source share

This overloads the stream operator. Yes, global variables are evil.

 #include <iostream> #include <string> #include <vector> #include <algorithm> #include <iterator> int index = 0; template<typename T, template <typename, typename> class Cont> std::ostream& operator<<(std::ostream& os, const Cont<T, std::allocator<T>>& vec) { if (index < vec.size()) { if (index + 1 < vec.size()) return os << vec[index++] << "-" << vec; else return os << vec[index++] << vec; } else return os; } int main() { std::vector<int> nums(10); int n{0}; std::generate(nums.begin(), nums.end(), [&]{ return n++; }); std::cout << nums << std::endl; } 
0
Feb 18 '14 at 2:28
source share

You can use functors:

 #include <functional> string getSeparatedValues(function<bool()> condition, function<string()> output, string separator) { string out; out += output(); while (condition()) out += separator + output(); return out; } 

Example:

 if (!keywords.empty()) { auto iter = keywords.begin(); cout << getSeparatedValues([&]() { return ++iter != keywords.end(); }, [&]() { return *iter; }, ", ") << endl; } 
0
Oct 25 '16 at 7:07
source share

Combination of lambda and macro in c ++ 11:

 #define INFIX_PRINTER(os, sep)([&]()->decltype(os)&{static int f=1;os<<(f?(f=0,""):sep);return os;})() 

Using:

 for(const auto& k: keywords) INFIX_PRINTER(out, ", ") << k; 
0
Aug 29 '19 at 7:24
source share



All Articles