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

69

我知道如何在其他语言中实现,但不知道如何在C++中实现,而这里必须使用C++。

我有一组字符串(keywords),需要作为列表打印到out,字符串之间需要逗号分隔,但不能有尾随逗号。例如在Java中,我会使用StringBuilder构建字符串后,删除末尾的逗号。在C++中应该怎么做呢?

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

我最初尝试插入以下代码块来完成它(将逗号打印移至此处):

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

8
我知道使用for(auto iter = ...;可以让代码行数更少,但除非你明确打算在循环之后使用它,否则你应该将iter与循环的作用域绑定。 - user229044
@πάνταῥεῖ 虽然它们是重复的,但为什么闭包不是另一种方式?这篇文章肯定看起来更像是一个更好的重复目标。 - Passer By
36个回答

1

我认为这应该可以工作

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

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

这行代码 ;iter++ 是做什么的?此外,这是有问题的——你重复添加了逗号。这会产生 word1,,word2,,word3, 这样的结果。 - Jamie Wong

1

可能是这样的..

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

为什么使用"条件语句"而不是"if语句"? - Potatoswatter
我喜欢简洁明了。但是我也可能被说服改变看法。 - JohnMcG
这个会每次修改 bFirst - Kerrek SB

1

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

1
这里有两种方法可供使用,它们本质上是相同的思路。我喜欢这些方法,因为它们不包含任何不必要的条件检查或赋值操作。我将第一种方法称为“先打印再处理的方法”。

方法1:先打印再处理的方法

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

这是我最初使用的方法。它通过先仅打印容器中的第一个元素,然后在每个后续元素前面加上逗号和空格来工作。它很简单、简洁,并且如果这就是你需要它做的全部事情,那么它非常好用。但是一旦你想要做更多的事情,例如在最后一个元素之前添加 "and",这种方法就显得不够了。你需要检查每次循环迭代是否在最后一个元素上。在列表后添加句点或换行并不是太糟糕。你只需在 for 循环后再添加一行来附加任何你想要的东西。
我更喜欢第二种方法。我称之为“打印最后一个元素的方法”,因为它与第一种方法相同,但顺序相反。
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.
}

这个方法通过打印除最后一个元素外的每个元素,并在其前面添加一个“and”,在其后面添加一个句点,和/或一个换行符,使您可以选择性地处理最后一个元素。正如您所看到的,这种方法为您提供了更多的选项,以便您可以处理最后一个元素,而不会影响循环的性能或添加太多代码。
如果让您不舒服留下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";
}

0

可以使用函数对象:

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

0
从C++23开始,您可以使用std::formatstd::print(或std::println)来完成此操作。
#include <print>
#include <vector>

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

[2, 3, 5, 7]

如果您需要将结果作为std::string使用std::format。请注意,格式化范围需要C++23支持,特别是实现P2286R8(请查看C++23库特性)。

#include <format>
#include <vector>

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

如果你被困在旧标准中,你可以使用fmt库来打印范围

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

0

在我看来,最流畅的解决方案:

std::vector<std::string> keywords = { "1", "2", "3", "4", "5" };
std::cout << std::accumulate(std::next(keywords.begin()), keywords.end(), *keywords.begin(),
                     [](const std::string& a, const std::string& b){ return a + ", " + b; }) << std::endl;

0

你可以使用 do 循环,在第一次迭代时重新编写循环条件,并使用短路运算符 && 以及有效流为 true 的事实。

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

似乎这会写两个逗号。 - Michael Mathews
@Michael:哎呀,复制粘贴的代码还留在里面了。已经修复了。 - Potatoswatter

0
我会选择像这样的东西,这是一个简单的解决方案,适用于所有迭代器。
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

这个函数重载了流操作符。是的,全局变量很邪恶。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接