我想这对所有的大师来说都可能是一个简单的问题,但我无法找到答案.
我希望能够像这样简单地编写csv单元格来进行流式处理:
stream << 1 << 2 << "Tom" << std::endl;
Run Code Online (Sandbox Code Playgroud)
这将产生1,2,汤姆等输出.我怎样才能做到这一点?我想我需要创建自定义streambuf(因为我不认为这是在流级别上执行它的正确方法,对于所有类型重载<<会是真正的痛苦)但是我不确定如何< <通常是实施的.它会调用put或write还是什么.我应该覆盖那些或什么?或者我完全错过了什么?
我很感激任何帮助:)
干杯,
Jer*_*fin 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;
}
Run Code Online (Sandbox Code Playgroud)
编辑:我已经修复了至少某种程度的代码 - 它现在只在写入的项目之间放置逗号,而不是在行的开头.但是,它只将"endl"识别为发出新记录的开头 - 例如,字符串文字中的换行符将不起作用.
虽然我可以理解重载流操作符的想法,但我会质疑手头问题的实践.
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;
};
Run Code Online (Sandbox Code Playgroud)
使用它仍然非常简单:
std::cout << Line(1,3,"Tom") << Line(2,4,"John") << Line(3,5,"Edward");
Run Code Online (Sandbox Code Playgroud)
想玩得开心吗?
现在,这可能看起来很沉闷,你可能希望玩,但仍然可以控制所写的内容......好吧,让我将模板元编程引入到战斗中;)
这是预期用途:
// 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
Run Code Online (Sandbox Code Playgroud)
现在那会有趣吗?它会格式化线并确保一定程度的验证......总是可以使设计复杂化以使其更多(例如为每个字段或整个线路注册验证器等)但是它已经足够复杂了.
// 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
Run Code Online (Sandbox Code Playgroud)
这里有一个流编写器,可以确保cvs文件格式正确...在字符串中删除换行符;)