C ++流操作符问题

时间:2010-03-04 02:07:10

标签: c++ csv stream

我想这对所有的大师来说可能都是一个简单的问题,但我无法找到答案。

我希望能够像这样简单地编写csv单元格:

stream << 1 << 2 << "Tom" << std::endl;

会创建像1,2,Tom这样的输出。我怎样才能做到这一点?我想我需要创建自定义streambuf(因为我不认为这是在流级别上执行它的正确方法,只是为了重载&lt;&lt;&lt;所有类型)真的很痛苦但是我不确定怎么&lt;&lt;通常是实施的。它会调用put或write还是什么。我应该覆盖那些或什么?或者我完全错过了什么?

我很感激任何帮助:)

干杯,

3 个答案:

答案 0 :(得分:10)

获得98%的方式并不是非常困难:

#include <iostream>

class add_comma { 
    std::ostream &os;
    bool begin;
    typedef add_comma &ref;
public:
    add_comma(std::ostream &o) : os(o), begin(true) {}

    template <class T>
    ref operator<<(T const &t) { 
        if (!begin)
            os << ",";
        os << "\"" << t << "\"";
        begin = false;
        return *this;
    }

    ref operator<<(std::ostream &manip(std::ostream &o) ) {
        if (&manip == &std::endl)
            reset();
        manip(os);
        return *this;
    }

    void reset() { begin = true; }

    operator void *() { return (void *)os; }
};

int main() { 
    add_comma a(std::cout);

    a << 1 << 2 << "This is a string" << std::endl;
    a << 3 << 4 << "Another string" << std::endl;
    return 0;
}

编辑:我已经修改了至少某种程度的代码 - 它现在只在写入的项目之间放置逗号,而不是在行的开头。但是,它只将“endl”识别为发出新记录的开头 - 例如,字符串文字中的换行符将不起作用。

答案 1 :(得分:5)

虽然我可以理解重载流操作符的想法,但我会质疑手头问题的实践。

<强> 1。面向对象的方法

如果您愿意使用.csv文件进行写入,那么每行的格式应该与其他行格式完全相同?不幸的是,您的流操作员不会检查它。

我认为您需要创建一个Line对象,而不是可流式传输,并且会在将每个字段写入文件之前对其进行验证(并使用正确的格式进行编写)。虽然不那么时髦,但你将有更多的机会在这里实现强大的实施。

假设您(例如)要输出2个整数和一个字符串:

class Line
{
public:
  Line(int foo, int bar, std::string firstName):
    mFoo(foo), mBar(bar), mFirstName(firstName)

  friend std::ostream& operator<<(std::ostream& out, const Line& line)
  {
    return out << line.mFoo << ',' << line.mBar << ','
               << line.mFirstName << std::endl;
  }
private:
  int mFoo;
  int mBar;
  std::string mFirstName;
};

使用它仍然非常简单:

std::cout << Line(1,3,"Tom") << Line(2,4,"John") << Line(3,5,"Edward");

<强> 2。想玩得开心吗?

现在,这可能看起来很乏味,你可能希望玩,但仍然可以控制所写的内容......好吧,让我将模板元编程引入到战斗中;)

以下是预期用途:

// Yeah, I could wrap this mpl_::vector bit... but it takes some work!
typedef CsvWriter< mpl_::vector<int,int,std::string> > csv_type;

csv_type(std::cout) << 1 << 3 << "Tom" << 2 << 4 << "John" << 3 << 5 << "Edward";

csv_type(std::cout) << 1 << 2 << 3; // Compile Time Error:
                                    // 3 is not convertible to std::string

现在那会有趣吗?它会格式化线并确保一定程度的验证......总是可以使设计复杂化以使其更多(例如为每个字段或整个线路注册验证器等等)但它已经足够复杂了。

// namespace mpl_ = boost::mpl

/// Sequence: MPL sequence
/// pos: mpl_::size_t<N>, position in the Sequence

namespace result_of {
  template <class Sequence, class pos> struct operator_in;
}

template < class Sequence, class pos = mpl_::size_t<0> >
class CsvWriter
{
public:
  typedef typename mpl_::at<Sequence,pos>::type current_type;
  typedef typename boost::call_traits<current_type>::param_type param_type;

  CsvWriter(std::ostream& out): mOut(out) {}

  typename result_of::operator_in<Sequence,pos>::type
  operator<<(param_type item)
  {
    typedef typename result_of::operator_in<Sequence,pos>::type result_type;

    if (pos::value != 0) mOut << ',';
    mOut << item;

    if (result_type::is_last_type::value) mOut << std::endl;              

    return result_type(mOut);
  }

private:
  std::ostream& mOut;
}; // class CsvWriter


/// Lil' bit of black magic
namespace result_of { // thanks Boost for the tip ;)

  template <class Sequence, class pos>
  struct operator_in
  {
    typedef typename boost::same_type<
        typename mpl_::size<Sequence>::type,
        typename mpl_::next<pos>::type
      > is_last_type;

    typedef typename mpl_::if_<
      is_last_type,
      CsvWriter< Sequence, mpl_::size_t<0> >,
      CsvWriter< Sequence, typename mpl_::next<pos>::type >
    >::type;
  }; // struct operator_in<Sequence,pos>

} // namespace result_of

这里有一个流编写器,可以确保cvs文件格式正确...在字符串中添加换行符;)

答案 2 :(得分:0)

如果A是元素的迭代器......

copy(A, A + N, ostream_iterator<int>(cout, ","));