如何打印逗号分隔的元素列表?

发布于 2025-01-24 19:25:13 字数 490 浏览 5 评论 0原文

我知道如何用其他语言来执行此操作,而不是在C ++中进行的,我在这里被迫使用。

我有一组字符串(关键字),我将其打印到out作为列表中,并且字符串之间需要逗号,但不需要逗号,但不需要拖延逗号。例如,在Java中,我将使用StringBuilder,然后在构建字符串后将逗号删除。我该如何在C ++中进行?

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

我最初尝试插入以下块进行操作(在此处移动逗号打印):

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

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

I have a set of strings (keywords) that I'm printing to out as a list, and the strings need a comma between them, 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 can I do it in C++?

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

或十年 2025-01-31 19:25:14

我认为简单性对我来说更好,因此,在浏览所有答案之后,我准备了解决方案(需要C ++ 14):

#include <iostream>
#include <vector>
#include <utility> // for std::exchange c++14

int main()
{    
    std::vector nums{1, 2, 3, 4, 5}; // c++17
    
    const char* delim = "";
    for (const auto value : nums)
    {
        std::cout << std::exchange(delim, ", ") << value;
    }
}

输出示例:

1, 2, 3, 4, 5

I think simplicity is better for me, so after I look through all answers I prepared my solution(c++14 required):

#include <iostream>
#include <vector>
#include <utility> // for std::exchange c++14

int main()
{    
    std::vector nums{1, 2, 3, 4, 5}; // c++17
    
    const char* delim = "";
    for (const auto value : nums)
    {
        std::cout << std::exchange(delim, ", ") << value;
    }
}

Output example:

1, 2, 3, 4, 5
情丝乱 2025-01-31 19:25:14

我建议您只需在Lambda的帮助下切换第一个角色即可。

std::function<std::string()> f = [&]() {f = [](){ return ","; }; return ""; };                  

for (auto &k : keywords)
    std::cout << f() << k;

I suggest you simply switch the first character with the help of a lambda.

std::function<std::string()> f = [&]() {f = [](){ return ","; }; return ""; };                  

for (auto &k : keywords)
    std::cout << f() << k;
时光清浅 2025-01-31 19:25:14

尝试以下操作:

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

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;
    }
}
何必那么矫情 2025-01-31 19:25:14

则避免了

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

另一个可能的解决方案,如果取决于您在循环中所做的事情,

Another possible solution, which avoids an if

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

Depends what you're doing in your loop.

流绪微梦 2025-01-31 19:25:14

如果值为std :: String S,则可以用 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;
}

对于必须转换为字符串的其他类型,您只需添加一个转换调用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;
}

If the values are std::strings you can write this nicely in a 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 which have to be converted to string you can just add a transformation calling 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;
}
如梦初醒的夏天 2025-01-31 19:25:14

您正在使用的++运算符有一个小问题。

您可以尝试:

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

这样,++将进行评估,然后再将迭代器与键words.end()进行比较。

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

You can try:

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

This way, ++ will be evaluated before compare the iterator with keywords.end().

ゝ杯具 2025-01-31 19:25:14

我使用一个小帮手课:

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;

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;
以歌曲疗慰 2025-01-31 19:25:14

以下应该这样做: -

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

Following should do:-

 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();
盛夏尉蓝 2025-01-31 19:25:14

我认为 @Markb答案的这种变体达到了最佳的可读性,简单性和简洁的平衡:

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

I think this variant of @MarkB's answer strikes optimal balance of readability, simplicity and terseness:

auto iter= keywords.begin();
if (iter!=keywords.end()) {
    out << *iter;
    while(++iter != keywords.end())
        out << "," << *iter;
}
out << endl;
晌融 2025-01-31 19:25:14

解决这个问题非常容易(取自我的答案 there ):

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

我在许多编程语言中使用此idiom(模式?),以及各种任务,您需要在其中构建诸如输入之类的列表中构造划界输出。让我在伪代码中给出摘要:

empty output
firstIteration = true
foreach item in list
    if firstIteration
        add delimiter to output
    add item to output
    firstIteration = false

在某些情况下,甚至可以完全省略priventeration指示器变量:

empty output
foreach item in list
    if not is_empty(output)
        add delimiter to output
    add item to output

It's very easy to fix that (taken from my answer here):

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

I am using this idiom (pattern?) in many programming languages, and all kind of tasks where you need to construct delimited output from list like inputs. Let me give the abstract in pseudo code:

empty output
firstIteration = true
foreach item in list
    if firstIteration
        add delimiter to output
    add item to output
    firstIteration = false

In some cases one could even omit the firstIteration indicator variable completely:

empty output
foreach item in list
    if not is_empty(output)
        add delimiter to output
    add item to output
┈┾☆殇 2025-01-31 19:25:14

从C ++ 23 开始,您可以使用 <代码> std ::格式 std ::::::::::: print (或 std :: println)来执行此操作。

#include <print>
#include <vector>

int main() {
    std::print("{}", std::vector{2, 3, 5, 7});
}
[2, 3, 5, 7]

如果您需要结果为std :: String使用 std ::格式 。请注意,格式化范围需要C ++ 23支持,特别是 p2286r8 (检查://en.cppreference.com/w/cpp/compiler_support#c.2b.2b.2b23_library_features“ rel =“ nofollow noreferrer”> c ++ 23库特征)。

#include <format>
#include <vector>

int main() {
    std::string text = std::format("{}", std::vector{2, 3, 5, 7});
}

如果您遇到了较旧的标准,则可以使用 fmt 库到 print ranges

#include <fmt/ranges.h>
#include <vector>

int main() {
    // direct print
    fmt::print("{}", std::vector{2, 3, 5, 7});

    // create std::string object
    std::string str = fmt::format("{}", std::vector{2, 3, 5, 7});
}

Starting with C++23 you can use std::format or std::print (or std::println) to do this.

#include <print>
#include <vector>

int main() {
    std::print("{}", std::vector{2, 3, 5, 7});
}
[2, 3, 5, 7]

If you need the result as std::string use std::format. Note that formatting ranges requires C++23 support, specifically the implementation of P2286R8 (check C++23 library features).

#include <format>
#include <vector>

int main() {
    std::string text = std::format("{}", std::vector{2, 3, 5, 7});
}

If you are stuck to an older standard, you can use the fmt library to print ranges.

#include <fmt/ranges.h>
#include <vector>

int main() {
    // direct print
    fmt::print("{}", std::vector{2, 3, 5, 7});

    // create std::string object
    std::string str = fmt::format("{}", std::vector{2, 3, 5, 7});
}
呆萌少年 2025-01-31 19:25:14

我认为这应该有效

while (iter != keywords.end( ))
{

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

I think this should work

while (iter != keywords.end( ))
{

    out << *iter;
    iter++ ;
    if (iter != keywords.end( )) out << ", ";
}
江城子 2025-01-31 19:25:14

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

您可以获得一个包含向量的字符串,即逗号分隔。

编辑:
要删除最后一个逗号,请发行:

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

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 obtain a string containing the vector, comma delimited.

EDIT:
to remove the last comma, just issue:

add_str = add_str.substr(0, add_str.size()-1);
爱人如己 2025-01-31 19:25:14

可能是这样..

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

Could be like so..

bool bFirst = true;
for (auto curr = keywords.begin();  curr != keywords.end(); ++curr) {
   std::cout << (bFirst ? "" : ", ") << *curr;
   bFirst = false;
}
┾廆蒐ゝ 2025-01-31 19:25:14

这是您可以使用的两种方法,它们本质上都是相同的想法。我喜欢这些方法,因为它们不包含任何不必要的条件检查或作业操作。我将第一个打印第一个方法。

方法1:打印第一个方法

if (!keywords.empty()) {
    out << *(keywords.begin()); // First element.
    for (auto it = ++(keywords.begin()); it != keywords.end(); it++)
        out << ", " << *it; // Every subsequent element.
}

这是我首先使用的方法。它可以通过单独打印容器中的第一个元素,然后打印出逗号和空间之前的每个后续元素。这很简单,简洁,如果您需要做的一切,效果很好。一旦您想做更多的事情,例如在最后一个元素之前添加“和”,此方法就会不足。您必须检查每个循环迭代是否在最后一个元素上。不过,添加一个期间或列表之后的新线并不那么糟糕。您可以在循环之后再添加一条线,以将您想要的任何内容附加到列表中。

我更喜欢第二种方法。我称​​ print last方法,因为它与第一个但相反顺序的事情相同。

方法2:打印最后一个方法,

if (!keywords.empty()) {
    auto it = keywords.begin(), last = std::prev(keywords.end());
    for (; it != last; it++) // Every preceding element.
        out << *it << ", ";
    out << "and " << *it << ".\n"; // Last element.
}

该方法是通过打印每个元素来起作用的方法,除了最后一个带有逗号和空间的元素,使您可以选择在其之前添加“和“”,一个后期和/或newline字符。如您所见,此方法为您提供了更多关于如何处理最后一个元素的选择,而不会影响循环的性能或添加很多代码。

如果您困扰您将for-loop空的第一部分留空,则可以这样写:

if (!keywords.empty()) {
    auto it, last;
    for (it = keywords.begin(), last = std::prev(keywords.end()); it != last; it++)
        out << *it << ", ";
    out << "and " << *it << ".\n";
}

Here are two methods you could use, which are both essentially the same idea. I like these methods because they do not contain any unnecessary conditional checks or assignment operations. I'll call the first one the print first method.

Method 1: the print first method

if (!keywords.empty()) {
    out << *(keywords.begin()); // First element.
    for (auto it = ++(keywords.begin()); it != keywords.end(); it++)
        out << ", " << *it; // Every subsequent element.
}

This is the method I used at first. It works by printing the first element in your container by itself, and then prints every subsequent element preceded by a comma and space. It's simple, concise, and works great if that's all you need it to do. Once you want to do more things, like add an "and" before the last element, this method falls short. You'd have to check each loop iteration for if it's on the last element. Adding a period, or newline after the list wouldn't be so bad, though. You could just add one more line after the for-loop to append whatever you desire to the list.

The second method I like a lot more. That one I'll call the print last method, as it does the same thing as the first but in reverse order.

Method 2: the print last method

if (!keywords.empty()) {
    auto it = keywords.begin(), last = std::prev(keywords.end());
    for (; it != last; it++) // Every preceding element.
        out << *it << ", ";
    out << "and " << *it << ".\n"; // Last element.
}

This one works by printing every element except for the last with a comma and space, allowing you to optionally add an "and" before it, a period after it, and/or a newline character. As you can see, this method gives you a lot more options on how you can handle that last element without affecting the performance of the loop or adding much code.

If it bothers you to leave the first part of the for-loop empty, you could write it like so:

if (!keywords.empty()) {
    auto it, last;
    for (it = keywords.begin(), last = std::prev(keywords.end()); it != last; it++)
        out << *it << ", ";
    out << "and " << *it << ".\n";
}
画▽骨i 2025-01-31 19:25:14

C ++ 20带来格式化库。但是截至目前(2021年4月),海湾合作委员会和叮当尚未实施它。但是我们可以使用 fmt 其基于以下位置的库

std::list<int> v{1, 2, 3, 4, 5};
fmt::print("{}", fmt::join(v, ", "));

C++20 brings the formatting library. However as of now (april 2021) neither gcc nor clang implement it yet. But we can use the fmt library on which it is based on:

std::list<int> v{1, 2, 3, 4, 5};
fmt::print("{}", fmt::join(v, ", "));
空城缀染半城烟沙 2025-01-31 19:25:14

我会选择这样的事情,一个简单的解决方案,应该适合所有迭代器。

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

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;
}
朱染 2025-01-31 19:25:14

您可以使用do循环,重写第一次迭代的循环条件,并使用短路&amp;&amp;&amp;操作员,并且有效流是<代码> true 。

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

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;
负佳期 2025-01-31 19:25:14

这使流操作员超载。是的,全球变量是邪恶的。

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

This one 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;
}
独自唱情﹋歌 2025-01-31 19:25:14

可以使用函子:

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

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;
}
荆棘i 2025-01-31 19:25:13

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

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, ","));
感受沵的脚步 2025-01-31 19:25:13

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

使用 clang 3.9 svn

In an experimental C++17 ready compiler coming soon to you, 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, ", "));
}

Live examples using GCC 6.0 SVN and Clang 3.9 SVN

烟燃烟灭 2025-01-31 19:25:13

因为每个人都决定在循环时使用此操作,所以我将举一个示例循环。

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

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

for (iter = keywords.begin(); iter != keywords.end(); iter++) {
  if (iter != keywords.begin()) cout << ", ";
  cout << *iter;
}
所有深爱都是秘密 2025-01-31 19:25:13

假设一个模糊的正常输出流,以便为其写一个空字符串确实没有任何作用:

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

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 = ", "
}
柒七 2025-01-31 19:25:13

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

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

编辑:
您还可以按照TED建议使用中间测试的环路,就像:

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

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

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.

美男兮 2025-01-31 19:25:13

有很多聪明的解决方案,太多的解决方案使代码超出了救赎的希望,而无需让编译器完成工作。

明显的解决方案是特殊案例:

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

这是一个死去的简单模式,它

  1. 不会构造循环:乍一看仍然很明显,每个元素都会迭代。
  2. 不仅允许放置分隔符或实际打印列表,因为else块,循环主体可以包含任意语句。

它可能不是绝对最有效的代码,但是单个良好预测的分支的潜在性能损失很可能会被std :: Ostream ::操作员&lt;&lt; 。

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

猥︴琐丶欲为 2025-01-31 19:25:13

这样的东西?

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

Something like this?

while (iter != keywords.end())
{
 out << *iter;
 iter++;
 if (iter != keywords.end()) cout << ", ";
}
难如初 2025-01-31 19:25:13

我进行分离器的典型方法(在中,任何语言)是使用中间测试的循环。 C ++代码将是:(

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

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

最终显示的大多数其他解决方案最终都会进行整个额外的测试。您正在做I/O,所以这并不是一个大问题,但它冒犯了我的敏感性。

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

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

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

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.

虐人心 2025-01-31 19:25:13

在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示例不同,其中“”是字符串,键>键字一个文明的字符串,在此C ++示例中,分隔符和关键字可以是任何流媒体

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

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

cout << join('\n', keywords) << endl;
氛圍 2025-01-31 19:25:13

为了避免将放置在循环中,我使用此操作:

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

这取决于矢量类型,int,但是您可以用一些助手将其删除。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文