问题 不要打印尾随分隔符stream_iterator C ++


在您看来,打印到最优雅的方式是什么? std::cout 运用 std::ostream_iterator 在C ++ 11中避免打印尾随分隔符?

我正在打印的对象具有双向迭代器,但不是随机访问迭代器。

std::list<double> x{1,2,3,4,5,6};
std::copy(x.begin(), std::prev(x.end()),
                std::ostream_iterator<int>(std::cout, ",") );
if ( x.size() != 0 )
  std::cout << *(--x.end()) << std::endl;

5407
2018-06-17 18:50


起源

“最优雅“按定义是主观的(读:不具有建设性)。 - ildjarn
@ildjarn我编辑过“在你看来”,但我觉得你的评论有点迂腐:你真的不认为这是一个有用的问题吗?我会写“最简洁”而不是“最优雅”,但是 en.wikipedia.org/wiki/Kolmogorov_complexity 是不可计算的。 ; ) - user
没有优雅的方法来做到这一点。你必须检查一些特殊情况。将其隐藏在一个函数中并继续前进。 - R. Martinho Fernandes
如果你已经有一个容器,你可以试试 漂亮的打印机。 - Kerrek SB
你应该看看 infix_iterator。 - Jesse Good


答案:


这是我的最爱之一,但它不使用 std::ostream_iterator

#include <iterator>
#include <string>
#include <iosfwd>

template <class C>
auto
print(std::ostream& os, const C& c,
      const std::string& delim = std::string(", "),
      const std::string& open_brace = std::string("{"),
      const std::string& close_brace = std::string("}")
     ) -> decltype(std::begin(c), std::end(c), os)
{
    os << open_brace;
    auto i = std::begin(c);
    auto e = std::end(c);
    if (i != e)
    {
        os << *i;
        for (++i; i != e; ++i)
            os << delim << *i;
    }
    os << close_brace;
    return os;
}

#include <list>
#include <iostream>

int main()
{
    std::list<double> x{1,2,3,4,5,6};
    print(std::cout, x) << '\n';
}

{1, 2, 3, 4, 5, 6}

更新

奥利弗引发了我无法抗拒的挑战。 :-)

#include <iterator>
#include <string>
#include <iosfwd>

namespace my {

template <class C>
auto
print(std::ostream& os, const C& c,
      const std::string& delim = std::string(", "),
      const std::string& open_brace = std::string("{"),
      const std::string& close_brace = std::string("}")
     ) -> decltype(std::begin(c), std::end(c), os);

template <class C,
           typename std::enable_if
                    <
                       !std::is_same<C, std::string>::value,
                    bool>::type = false
         >
inline
auto
operator<< (std::ostream& os, const C& c) -> decltype(print(os, c))
{
    return print(os, c);
}

template <class C>
auto
print(std::ostream& os, const C& c,
      const std::string& delim,
      const std::string& open_brace,
      const std::string& close_brace
     ) -> decltype(std::begin(c), std::end(c), os)
{
    os << open_brace;
    auto i = std::begin(c);
    auto e = std::end(c);
    if (i != e)
    {
        os << *i;
        for (++i; i != e; ++i)
            os << delim << *i;
    }
    os << close_brace;
    return os;
}

}

#include <list>
#include <forward_list>
#include <iostream>

int main()
{
    std::forward_list<std::list<double>> x{{}, {3, 2, 1}, {1,2,3,4,5,6}};
    my::print(std::cout, x) << '\n';
}

{{}, {3, 2, 1}, {1, 2, 3, 4, 5, 6}}

它并不完美,但很有趣。 :-)可能有更好的方法来传播自定义分隔符和更忠实的括号。


9
2018-06-17 19:11



+1这是否有一个调整,以便它可以打印 list<list<double> >?如果它们是容器,也许递归地调用内部对象上的print? - user
我可以问为什么会这样 decltype(std::begin(c), std::end(c), os) 对于?不能返回值 std::ostream&? - mfontanini
@Oliver:是的,递归调用内部对象上的print可能是要走的路。为了好玩,我把一个替代品放在一起。 - Howard Hinnant
@HowardHinnant超酷!这对于调试来说非常好(我总是把自己说成只是输入嵌套的for循环“就这一次”。:) - user
@mfontanini:我想限制 print<C> 功能只适用于那些类型 std::begin(c) 和 std::end(c) 是合法的。这将使我以后(例如)创建一个 print(std::ostream&, double) 重载并且不必担心调用此新重载将被错误地路由到容器版本 print(std::ostream&, const C&)。即如果 decltype 在尾随返回中对于特定的实例化无效 C那个实例化就是SFINAE了。 - Howard Hinnant


只需将光标向后移动:

std::cout << "\b";

然后覆盖分隔符。


4
2018-01-03 23:09





C ++ 14 - 遗憾的是需要输入/输出流:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <list>

int main()
{
    std::list<double> x{1,2,3,4,5,6};
    std::stringstream sstr;
    std::transform(x.begin(), x.end(), std::ostream_iterator<std::string>(sstr), [&](auto c){
        return (sstr.rdbuf()->in_avail() ? "," : "") + std::to_string(c);
    });
    std::cout << sstr.str() << std::endl;
}

生活

从C ++ 17开始 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, ", "));
}

生活


1
2018-03-21 19:15