用逗号 C++ 打印列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3496982/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 12:58:48  来源:igfitidea点击:

Printing lists with commas C++

c++pretty-print

提问by quandrum

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

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

I have a Set of Strings that I'm printing to out in a list, and they need a comma between each one, but not a trailing comma. In java for instance, I would use a stringbuilder and just delete the comma off the end after I've built my string. How do I do it in C++?

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

auto iter = keywords.begin();
for (iter; iter != keywords.end( ); iter++ )
{

    out << *iter << ", ";
}
out << endl;

I initially tried inserting this block to do it (moving the comma printing here)

我最初尝试插入此块来执行此操作(将逗号打印移至此处)

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

I hate when the small things trip me up.

我讨厌小事绊倒我。

EDIT: Thanks everyone. This is why I post stuff like this here. So many good answers, and tackled in different ways. After a semester of Java and assembly (different classes), having to do a C++ project in 4 days threw me for a loop. Not only did I get my answer, I got a chance to think about the different ways to approach a problem like this. Awesome.

编辑:谢谢大家。这就是为什么我在这里发布这样的东西。这么多好的答案,并以不同的方式解决。经过一学期的 Java 和汇编(不同的课程),不得不在 4 天内完成一个 C++ 项目让我陷入了困境。我不仅得到了答案,而且有机会思考解决此类问题的不同方法。惊人的。

采纳答案by Jerry Coffin

Use an 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 

Usage would be something like:

用法类似于:

#include "infix_iterator.h"

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

回答by TemplateRex

In an experimental C++17 ready compiler coming soon to you, you can use std::experimental::ostream_joiner:

在即将推出的实验性 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, ", "));
}

Live examples using GCC 6.0 SVNand Clang 3.9 SVN

使用GCC 6.0 SVNClang 3.9 SVN 的现场示例

回答by Jamie Wong

Because everyone has decided to do this with while loops, I'll give an example with for loops.

因为每个人都决定用 while 循环来做这件事,所以我将举一个 for 循环的例子。

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

回答by Steve Jessop

Assuming a vaguely normal output stream, so that writing an empty string to it does indeed do nothing:

假设一个模糊正常的输出流,那么向它写入一个空字符串确实什么都不做:

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

回答by Mark B

One common approach is to print the first item prior to the loop, and loop only over the remaining items, PRE-printing a comma before each remaining item.

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

Alternately you should be able to create your own stream that maintains a current state of the line (before endl) and puts commas in the appropriate place.

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

EDIT: You can also use a middle-tested loop as suggested by T.E.D. It would be something like:

编辑:您也可以使用 TED 建议的中间测试循环它会是这样的:

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

I mentioned the "print first item before loop" method first because it keeps the loop body really simple, but any of the approaches work fine.

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

回答by Matthieu M.

There are lots of clever solutions, and too many that mangle the code beyond hope of salvation without letting the compiler do its job.

有很多聪明的解决方案,有太多的方法在不让编译器完成其工作的情况下将代码破坏得无望。

Theobvious solution, is to special-case the first iteration:

明显的解决方案,是特殊情况下的第一次迭代:

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

It's a dead simple pattern which:

这是一个非常简单的模式:

  1. Does not mangle the loop: it's still obvious at a glance that each element will be iterated on.
  2. Allows more than just putting a separator, or actually printing a list, as the elseblock and the loop body can contain arbitrary statements.
  1. 不会破坏循环:每个元素都将被迭代,一目了然。
  2. 不仅允许放置分隔符或实际打印列表,因为else块和循环体可以包含任意语句。

It may not be the absolutely most efficient code, but the potential performance loss of a single well-predicted branch is very likely to be overshadowed by the massive behemoth that is std::ostream::operator<<.

它可能不是绝对最高效的代码,但单个预测良好的分支的潜在性能损失很可能被std::ostream::operator<<.

回答by Klark

Something like this?

像这样的东西?

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

回答by T.E.D.

My typical method for doing separators (in any language) is to use a mid-tested loop. The C++ code would be:

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

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

(note: An extra ifcheck is needed prior to the loop if keywords may be empty)

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

Most of the other solutions shown end up doing an entire extra test every loop iteration. You are doing I/O, so the time taken by that isn't a huge problem, but it offends my sensibilities.

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

回答by Darko Veberic

In python we just write:

在python中我们只写:

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 in the python example above where the " "is a string and the keywordshas to be an iterable of strings, here in this C++ example the separator and keywordscan be anything streamable, e.g.

与上面的python示例不同,the " "is a string and the must keywordsbe an iterable of strings,在这个C++示例中,分隔符keywords可以是任何可流式传输的,例如

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

回答by Martin York

Try this:

尝试这个:

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