Printing lists with commas C++

2018-12-31 07:54发布

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

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++?

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.

24条回答
大哥的爱人
2楼-- · 2018-12-31 08:33

I use a little helper class for that:

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;
查看更多
后来的你喜欢了谁
3楼-- · 2018-12-31 08:33

to avoid placing an 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 vector type, int, but you can remove it with some helper.

查看更多
弹指情弦暗扣
4楼-- · 2018-12-31 08:34

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

The obvious 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 else block and the loop body can contain arbitrary statements.

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<<.

查看更多
零度萤火
5楼-- · 2018-12-31 08:34

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

auto iter = keywords.begin();
if ( ! keywords.empty() ) do {
    out << * iter;
} while ( ++ iter != keywords.end() && out << ", " );
out << endl;
查看更多
永恒的永恒
6楼-- · 2018-12-31 08:35

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.

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

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.

查看更多
何处买醉
7楼-- · 2018-12-31 08:37

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 = ", "
}
查看更多
登录 后发表回答