用逗号C打印列表

我知道如何做到这一点在其他语言,但不是C ++,我不得不在这里使用。

我有一组string,我打印在一个列表中,每个字符之间需要一个逗号,而不是尾随的逗号。 例如在java中,我会使用一个stringbuilder,并在我build立我的string后删除逗号。 我如何在C ++中做到这一点?

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

我最初尝试插入此块来做到这一点(在这里移动逗号打印)

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

我讨厌什么时候小事让我起来。

编辑:谢谢大家。 这就是为什么我在这里发布这样的东西。 这么多好的答案,以不同的方式解决。 在Java和程序集(不同的类)的一个学期之后,必须在4天内完成一个C ++项目,让我循环。 我不仅得到了答案,还有机会思考了解决这个问题的不同方法。 真棒。

使用infix_iterator:

 // infix_iterator.h // // Lifted from Jerry Coffin's 's 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's 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 

用法如下所示:

 #include "infix_iterator.h" // ... std::copy(keywords.begin(), keywords.end(), infix_iterator(out, ",")); 

因为每个人都已经决定用while循环来做这个事情,所以我将给出一个for循环的例子。

 for (iter = keywords.begin(); iter != keywords.end(); iter++) { if (iter != keywords.begin()) cout << ", "; cout << *iter; } 

一种常用的方法是在循环之前打印第一个项目,并仅循环其余项目,在每个剩余项目之前预先打印一个逗号。

或者,你应该能够创build自己的stream,保持行的当前状态(endl之前),并将逗号放在适当的位置。

编辑:你也可以使用TEDbuild议的中间testing循环它会是这样的:

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

我首先提到了“在循环之前先打印第一项”的方法,因为它使循环体非常简单,但是任何一种方法都能正常工作。

在即将推出的实验性C ++ 17编译器中,您可以使用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, ", ")); } 

使用GCC 6.0 SVNClang 3.9 SVN的实例

假设一个模糊的正常输出stream,那么写一个空string确实没有任何作用:

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

像这样的东西?

 while (iter != keywords.end()) { out << *iter; iter++; if (iter != keywords.end()) cout << ", "; } 

我做分隔符(使用任何语言 )的典型方法是使用中间testing的循环。 C ++代码将是:

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

(注意:如果关键字可能为空,则在循环之前需要额外的检查)

所显示的大多数其他解决scheme最终都会在每次循环迭代中进行一次完整的额外testing 你在做I / O,所以花费的时间不是一个很大的问题,但它触犯了我的感情。

尝试这个:

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

你正在使用的++操作符有一个小问题。

你可以试试:

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

这样,在将迭代器与keywords.end()进行比较之前,将对++进行评估。

有很多聪明的解决scheme,而且太多的代码在编译器没有完成工作的情况下就无法超越救赎的希望。

明显的解决办法是特殊情况下的第一次迭代:

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

这是一个死的简单模式,其中:

  1. 不会破坏循环:一眼就能看出每个元素将被迭代。
  2. 不仅仅是把一个分隔符,或者实际上打印一个列表作为else块,并且循环体可以包含任意的语句。

它可能不是绝对最有效的代码,但是一个预测好的分支潜在的性能损失很可能被std::ostream::operator<<巨大的巨兽所掩盖。

我认为这应该工作

 while (iter != keywords.end( )) { out << *iter; iter++ ; if (iter != keywords.end( )) out << ", "; } 

我使用一个小辅助类:

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

要使用它:

 text_separator sep(", "); for (int i = 0; i < 10; ++i) cout << sep() << i; 

使用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)); 

并获得一个包含逗号分隔的vector的string。

编辑:删除最后一个逗号,只是问题:

 add_str = add_str.substr(0, add_str.size()-1); 

在Python中,我们只写:

 print ", ".join(keywords) 

那为什么不呢:

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

然后就像使用它:

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

和上面的python例子不同的是, " "是一个string, keywords必须是可迭代的string,在这个C ++的例子中,分隔符和keywords可以是任何可以stream传的东西,例如

 cout << join('\n', keywords) << endl; 

可能是这样的..

 bool bFirst = true; for (auto curr = keywords.begin(); curr != keywords.end(); ++curr) { std::cout << (bFirst ? "" : ", ") << *curr; bFirst = false; } 

为了避免放置一个if在循环内,我使用这个:

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

它取决于vectortypesint ,但是可以用一些帮助器将其删除。

以下应该做的: –

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

我会去这样的事情,一个简单的解决scheme,应该适用于所有的迭代器。

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

您可以使用do循环,重写第一次迭代的循环条件,并使用短路&&运算符以及有效stream为true

 auto iter = keywords.begin(); if ( ! keywords.empty() ) do { out << * iter; } while ( ++ iter != keywords.end() && out << ", " ); out << endl; 

这一个重载stream操作符。 是的全局variables是邪恶的。

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

另一个可能的解决scheme,它可以避免一个if

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

取决于你在循环中做什么。

可以使用函子:

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

例:

 if (!keywords.empty()) { auto iter = keywords.begin(); cout << getSeparatedValues([&]() { return ++iter != keywords.end(); }, [&]() { return *iter; }, ", ") << endl; } 

我build议你只要在lambda的帮助下切换第一个字符。

 std::function<std::string()> f = [&]() {f = [](){ return ","; }; return ""; }; for (auto &k : keywords) std::cout << f() << k;