C++11:借助std::tuple实现CSV的读写

一直想写个CSV读写的模板,但是接口的定义一直是个问题。CSV每列都可以任意映射成C++基本类型,怎么把接口定义得既通用又灵活?最近发现C++11引入了tuple,接口也可以像python一样的方便漂亮,而且不再依赖boost库,真正做到Windows/Linux一套代码。


#ifndef _CSV_TO_TUPLES_H
#define _CSV_TO_TUPLES_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "common/common_utils.h"

namespace util {
namespace csv_tuple {


template 
class NullType
{
public:
    explicit NullType()
        : is_null(true) // by default, NULL value
    {}

    explicit NullType(const NullType& src)
        : is_null(src.is_null), value(src.value)
    {}

    explicit NullType(const T& v)
        : is_null(false), value(v)
    {}

    ~NullType()
    {}

    void Reset()
    {
        // not all types can be reset by assigning of 0
        *this = NullType();
    }

    NullType& operator=(const NullType& src)
    {
        this->is_null = src.is_null;
        this->value = src.value;
        return *this;
    }

public:
    bool is_null;
    T value;
};

typedef NullType NullChar;
typedef NullType NullUChar;
typedef NullType NullShort;
typedef NullType NullUShort;
typedef NullType NullInt;
typedef NullType NullUInt;
typedef NullType NullBigInt;
typedef NullType NullUBigInt;
typedef NullType NullFloat;
typedef NullType NullReal;
typedef NullType NullDouble;

typedef NullType NullBool;
typedef NullType NullString;


template 
void StringToValue(const std::string& str, NullType& value)
{
    if (str.empty()) {
        value.Reset();
    }
    else {
        value.is_null = false;
        StringToValue(str, value.value);
    }
}

static inline void StringToValue(const std::string &str, char &value)
{
    value = (char)atoi(str.c_str());
}

static inline void StringToValue(const std::string &str, unsigned char &value)
{
    value = (unsigned char)atoi(str.c_str());
}

static inline void StringToValue(const std::string &str, short &value)
{
    value = (short)atoi(str.c_str());
}

static inline void StringToValue(const std::string &str, unsigned short &value)
{
    value = (unsigned short)atoi(str.c_str());
}

static inline void StringToValue(const std::string &str, int &value)
{
    value = atoi(str.c_str());
}

static inline void StringToValue(const std::string &str, unsigned int &value)
{
    value = (unsigned int)std::strtoul(str.c_str(), NULL, 10);
}

static inline void StringToValue(const std::string &str, long long &value)
{
#ifdef _WIN32
    value = _atoi64(str.c_str());
#else
    value = atoll(str.c_str());
#endif
}

static inline void StringToValue(const std::string &str, unsigned long long &value)
{
    value = (unsigned long long)std::strtoull(str.c_str(), NULL, 10);
}

static inline void StringToValue(const std::string &str, bool &value)
{
    value = atoi(str.c_str()) != 0;
}

static inline void StringToValue(const std::string& str, std::string& value)
{
    value = str;
}

static inline void StringToValue(const std::string& str, float& value)
{
    value = (float)atof(str.c_str());
}

static inline void StringToValue(const std::string& str, double& value)
{
    value = atof(str.c_str());
}

// add more versions of StringToValue() if needed


#define TUPLE_SET_ITEM(tp, i) \
    T##i t##i; \
    StringToValue(subs[i], t##i); \
    std::get(tp) = t##i

template
void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
}

template
void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
}

template
void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
}

template
void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
}

template
void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
}

template
void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
    TUPLE_SET_ITEM(tp, 5);
}

template
    void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
    TUPLE_SET_ITEM(tp, 5);
    TUPLE_SET_ITEM(tp, 6);
}

template
    void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
    TUPLE_SET_ITEM(tp, 5);
    TUPLE_SET_ITEM(tp, 6);
    TUPLE_SET_ITEM(tp, 7);
}

template
    void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
    TUPLE_SET_ITEM(tp, 5);
    TUPLE_SET_ITEM(tp, 6);
    TUPLE_SET_ITEM(tp, 7);
    TUPLE_SET_ITEM(tp, 8);
}

template
    void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
    TUPLE_SET_ITEM(tp, 5);
    TUPLE_SET_ITEM(tp, 6);
    TUPLE_SET_ITEM(tp, 7);
    TUPLE_SET_ITEM(tp, 8);
    TUPLE_SET_ITEM(tp, 9);
}

template
    void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
    TUPLE_SET_ITEM(tp, 5);
    TUPLE_SET_ITEM(tp, 6);
    TUPLE_SET_ITEM(tp, 7);
    TUPLE_SET_ITEM(tp, 8);
    TUPLE_SET_ITEM(tp, 9);
    TUPLE_SET_ITEM(tp, 10);
}

template
    void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
    TUPLE_SET_ITEM(tp, 5);
    TUPLE_SET_ITEM(tp, 6);
    TUPLE_SET_ITEM(tp, 7);
    TUPLE_SET_ITEM(tp, 8);
    TUPLE_SET_ITEM(tp, 9);
    TUPLE_SET_ITEM(tp, 10);
    TUPLE_SET_ITEM(tp, 11);
}

template
    void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
    TUPLE_SET_ITEM(tp, 5);
    TUPLE_SET_ITEM(tp, 6);
    TUPLE_SET_ITEM(tp, 7);
    TUPLE_SET_ITEM(tp, 8);
    TUPLE_SET_ITEM(tp, 9);
    TUPLE_SET_ITEM(tp, 10);
    TUPLE_SET_ITEM(tp, 11);
    TUPLE_SET_ITEM(tp, 12);
}

template
    void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
    TUPLE_SET_ITEM(tp, 5);
    TUPLE_SET_ITEM(tp, 6);
    TUPLE_SET_ITEM(tp, 7);
    TUPLE_SET_ITEM(tp, 8);
    TUPLE_SET_ITEM(tp, 9);
    TUPLE_SET_ITEM(tp, 10);
    TUPLE_SET_ITEM(tp, 11);
    TUPLE_SET_ITEM(tp, 12);
    TUPLE_SET_ITEM(tp, 13);
}

template
    void FillTuple(const std::vector& subs,
    std::tuple& tp)
{
    TUPLE_SET_ITEM(tp, 0);
    TUPLE_SET_ITEM(tp, 1);
    TUPLE_SET_ITEM(tp, 2);
    TUPLE_SET_ITEM(tp, 3);
    TUPLE_SET_ITEM(tp, 4);
    TUPLE_SET_ITEM(tp, 5);
    TUPLE_SET_ITEM(tp, 6);
    TUPLE_SET_ITEM(tp, 7);
    TUPLE_SET_ITEM(tp, 8);
    TUPLE_SET_ITEM(tp, 9);
    TUPLE_SET_ITEM(tp, 10);
    TUPLE_SET_ITEM(tp, 11);
    TUPLE_SET_ITEM(tp, 12);
    TUPLE_SET_ITEM(tp, 13);
    TUPLE_SET_ITEM(tp, 14);
}

// add more versions of FillTuple() if needed

#undef TUPLE_SET_ITEM


template 
static inline std::string ValueToStr(const T& value)
{
    std::stringstream ss;
    ss << value;
    std::string str;
    ss >> str;
    return str;
}

template <>
std::string ValueToStr(const double& value)
{
    std::stringstream ss;
    ss << std::setprecision(11) << value;
    std::string str;
    ss >> str;
    return str;
}

template <>
std::string ValueToStr(const float& value)
{
    std::stringstream ss;
    ss << std::setprecision(11) << value;
    std::string str;
    ss >> str;
    return str;
}

template <>
std::string ValueToStr(const std::string& value)
{
    std::string ret("\"");
    ret += value;
    ret += '\"';
    return std::move(ret);
}

template
void FillLine(const std::tuple& tp, char /*delimiter*/, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
}

template
void FillLine(const std::tuple& tp, char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
}

template
void FillLine(const std::tuple& tp, char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
}

template
void FillLine(const std::tuple& tp, char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
}

template
void FillLine(const std::tuple& tp, char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
}

template
void FillLine(const std::tuple& tp, char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
    line += delimiter;
    line += ValueToStr(std::get<5>(tp));
}

template
void FillLine(const std::tuple& tp, char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
    line += delimiter;
    line += ValueToStr(std::get<5>(tp));
    line += delimiter;
    line += ValueToStr(std::get<6>(tp));
}

template
    void FillLine(const std::tuple& tp, char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
    line += delimiter;
    line += ValueToStr(std::get<5>(tp));
    line += delimiter;
    line += ValueToStr(std::get<6>(tp));
    line += delimiter;
    line += ValueToStr(std::get<7>(tp));
}

template
    void FillLine(const std::tuple& tp,
    char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
    line += delimiter;
    line += ValueToStr(std::get<5>(tp));
    line += delimiter;
    line += ValueToStr(std::get<6>(tp));
    line += delimiter;
    line += ValueToStr(std::get<7>(tp));
    line += delimiter;
    line += ValueToStr(std::get<8>(tp));
}

template
    void FillLine(const std::tuple& tp,
    char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
    line += delimiter;
    line += ValueToStr(std::get<5>(tp));
    line += delimiter;
    line += ValueToStr(std::get<6>(tp));
    line += delimiter;
    line += ValueToStr(std::get<7>(tp));
    line += delimiter;
    line += ValueToStr(std::get<8>(tp));
    line += delimiter;
    line += ValueToStr(std::get<9>(tp));
}

template
    void FillLine(const std::tuple& tp,
    char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
    line += delimiter;
    line += ValueToStr(std::get<5>(tp));
    line += delimiter;
    line += ValueToStr(std::get<6>(tp));
    line += delimiter;
    line += ValueToStr(std::get<7>(tp));
    line += delimiter;
    line += ValueToStr(std::get<8>(tp));
    line += delimiter;
    line += ValueToStr(std::get<9>(tp));
    line += delimiter;
    line += ValueToStr(std::get<10>(tp));
}

template
    void FillLine(const std::tuple& tp,
    char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
    line += delimiter;
    line += ValueToStr(std::get<5>(tp));
    line += delimiter;
    line += ValueToStr(std::get<6>(tp));
    line += delimiter;
    line += ValueToStr(std::get<7>(tp));
    line += delimiter;
    line += ValueToStr(std::get<8>(tp));
    line += delimiter;
    line += ValueToStr(std::get<9>(tp));
    line += delimiter;
    line += ValueToStr(std::get<10>(tp));
    line += delimiter;
    line += ValueToStr(std::get<11>(tp));
}

template
    void FillLine(const std::tuple& tp,
    char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
    line += delimiter;
    line += ValueToStr(std::get<5>(tp));
    line += delimiter;
    line += ValueToStr(std::get<6>(tp));
    line += delimiter;
    line += ValueToStr(std::get<7>(tp));
    line += delimiter;
    line += ValueToStr(std::get<8>(tp));
    line += delimiter;
    line += ValueToStr(std::get<9>(tp));
    line += delimiter;
    line += ValueToStr(std::get<10>(tp));
    line += delimiter;
    line += ValueToStr(std::get<11>(tp));
    line += delimiter;
    line += ValueToStr(std::get<12>(tp));
}

template
    void FillLine(const std::tuple& tp,
    char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
    line += delimiter;
    line += ValueToStr(std::get<5>(tp));
    line += delimiter;
    line += ValueToStr(std::get<6>(tp));
    line += delimiter;
    line += ValueToStr(std::get<7>(tp));
    line += delimiter;
    line += ValueToStr(std::get<8>(tp));
    line += delimiter;
    line += ValueToStr(std::get<9>(tp));
    line += delimiter;
    line += ValueToStr(std::get<10>(tp));
    line += delimiter;
    line += ValueToStr(std::get<11>(tp));
    line += delimiter;
    line += ValueToStr(std::get<12>(tp));
    line += delimiter;
    line += ValueToStr(std::get<13>(tp));
}

template
    void FillLine(const std::tuple& tp,
    char delimiter, std::string& line)
{
    line = ValueToStr(std::get<0>(tp));
    line += delimiter;
    line += ValueToStr(std::get<1>(tp));
    line += delimiter;
    line += ValueToStr(std::get<2>(tp));
    line += delimiter;
    line += ValueToStr(std::get<3>(tp));
    line += delimiter;
    line += ValueToStr(std::get<4>(tp));
    line += delimiter;
    line += ValueToStr(std::get<5>(tp));
    line += delimiter;
    line += ValueToStr(std::get<6>(tp));
    line += delimiter;
    line += ValueToStr(std::get<7>(tp));
    line += delimiter;
    line += ValueToStr(std::get<8>(tp));
    line += delimiter;
    line += ValueToStr(std::get<9>(tp));
    line += delimiter;
    line += ValueToStr(std::get<10>(tp));
    line += delimiter;
    line += ValueToStr(std::get<11>(tp));
    line += delimiter;
    line += ValueToStr(std::get<12>(tp));
    line += delimiter;
    line += ValueToStr(std::get<13>(tp));
    line += delimiter;
    line += ValueToStr(std::get<14>(tp));
}
}

template
bool CsvToTuples(std::istream& in, char delimiter, std::vector >& tuples,
    std::string& err)
{
    const size_t N = sizeof...(Args);
    std::string line;
    std::vector subs;

    std::tuple tp;
    while (util::GetLine(in, line)) {
        if (line[0] == '#') { // ignore comment
            continue;
        }

        util::ParseCsvLine(subs, line, delimiter);
        if (subs.size() < N) {
            std::ostringstream oss;
            oss << "Error: unexpected column size. Expected: " << N << ", Actual: " << subs.size()
                << ", line data : " << line;
            err = oss.str();
            return false;
        }

        csv_tuple::FillTuple(subs, tp);
        tuples.push_back(tp);
    }

    return true;
}

template
bool CsvToTuples(const std::string& csv_pathname, char delimiter,
    std::vector >& tuples, std::string& err)
{
    std::ifstream in(csv_pathname.c_str());
    if (!in.good()) {
        err = "Error in opening file \"" + csv_pathname + "\"";
        return false;
    }

    return CsvToTuples(in, delimiter, tuples, err);
}

template
bool TuplesToCsv(const std::string& head, std::vector >& tuples,
    std::ostream& out, char delimiter, std::string& err)
{
    if (!head.empty()) {
        out << head << std::endl;
    }

    const size_t N = sizeof...(Args);
    std::string line;

    const size_t buff_size = 1024 * 8;
    std::string buff;
    buff.reserve(buff_size + 256);

    for (auto& t : tuples) {
        csv_tuple::FillLine(t, delimiter, line);
        buff += line;
        buff += '\n';
        if (buff.length() > buff_size) {
            out << buff;
            buff.clear();
        }
    }
    if (!buff.empty()) {
        out << buff;
    }

    return true;
}

template
bool TuplesToCsv(const std::string& head, std::vector >& tuples,
    const std::string& csv_pathname, char delimiter, std::string& err)
{
    std::ofstream out(csv_pathname.c_str());
    if (!out.good()) {
        err = "Error in opening file \"" + csv_pathname + "\"";
        return false;
    }

    return TuplesToCsv(head, tuples, out, delimiter, err);
}

}

#endif //_CSV_TO_TUPLES_H



写文件调用示例。读文件的略。


    typedef std::tuple out_entry;
    std::vector tuples;
    tuples.push_back(std::make_tuple("ABC", "", 100.1, 100));
    std::string head("# COLUMN1,COLUMN2,COLUMN3,COLUMN4"), err;
    TuplesToCsv(head, tuples, csv, ',' , err);



你可能感兴趣的:(C++)