之前提到了自定义的Csv格式的表格读取的一个工具类CsvReader
这里我们实现一个可以对任意符合我们人为规定的格式的Csv文件,自动生成其对应的读表代码
本工具需要boost库支持,本人用的是1.55.0
这里首先定义Csv中支持的几种列类型:FieldType:包括有/无符号整数,小数,字符串,子结构
enum FieldType
{
FT_int = 0, //整数
FT_uint, //整数
FT_float, //浮点数
FT_string, //字符串
FT_struct, //子结构
FT_unknow,
};
列类型为子结构时,对于子结构信息的定义:ChildStruct:包括字段名,字段类型
class ChildStruct
{
public:
ChildStruct();
ChildStruct(const ChildStruct &other);
ChildStruct& operator = (const ChildStruct &other);
~ChildStruct();
std::vector<std::string> m_arrChildName; // 子结构的字段名称
std::vector<FieldType> m_arrFieldType; // 子结构的字段类型
};
整个表格文件,前3行涉及的信息的定义:TableFile:包括表名称,列名称,列类型,注释,是否是主键,是否是数组
class CsvFile
{
public:
CsvFile();
CsvFile(const CsvFile &other);
CsvFile& operator = (const CsvFile &other);
~CsvFile();
std::string m_strClassName; // 类名称
std::vector<std::string> m_arrName; // 列名称(原始名称)
std::vector<std::string> m_arrType; // 列类型标识字符串
std::vector<std::string> m_arrComments; // 列注释
std::vector<int> m_arrKey; // 主键列下标
std::vector<bool> m_arrIsList; // 列是否是不定长数组(lst)
std::vector<FieldType> m_arrFieldType; // 列类型枚举
std::map<int, ChildStruct> m_mapChildStruct;// 嵌套的子结构字段名称,类型映射表
};
Csv文件解析类,读取文件文本内容,按相应配置(命名空间,文件路径等),解析生成相应的CsvFile对象,
class CsvParse
{
public:
CsvParse();
~CsvParse();
bool ParseFile(const std::string& fullPathName);
// 解析完毕的csv文件信息列表
std::map<std::string, CsvFile> m_Files;
// csv文件解析工具输入参数
public:
std::string m_NameSpace; // 自定义命名空间
bool m_ThreadSpecific; // 是否需要线程本地表格数据
std::vector<std::string> m_CsvFileName; // 待处理的csv文件列表
std::string m_LoaderPath; // loader输出cpp代码路径
std::string m_HelperPath; // helper输出cpp代码路径
};
传入一个Csv的文件完整路径,调用CsvReader,读取出列名称,列类型,列注释
对于列名称,调用StringTool进行首字母大写
对于列类型,按照自定义Csv格式解析出对应类型或子结构
对于列注释,如果是Utf8,则调用StringTool转换成GBK
具体的代码生成包含两部分功能:
void ParseCsv2LoaderH(const CsvParse& csvParse)
void ParseCsv2LoaderCpp(const CsvParse& csvParse)
void ParseCsv2HelperH(const CsvParse& csvParse)
void ParseCsv2HelperCpp(const CsvParse& csvParse)
传入一个解析好的CsvParse对象,生成具体代码
Loader生成对应的读取代码,读取csv文件的每一行数据,转成相应的数据结构。
Helper生成一个空的辅助类,用于需要对数据重新组织的情况,比如配置中是按某个id列作为主键,存成一个map,提供查找,但是实际逻辑中还需要按某个名称列作为索引,再提供一个map,用于提供按名称查找(或者模糊查找),这时候就需要手动再提供一个新的查找函数,就可以写在helper中
需要生成的代码包括:
// 1、头文件:map,vector等
//
// 2、命名空间(由程序运行参数决定)
//
// 3、每个子结构:class XXX {...};
// 包括:
// 构造:XXX() {...}
// 拷贝构造:XXX(const XXX &other) {...}
// 赋值:XXX& operator = (const XXX &other) {...}
// 赋值:XXX& operator = (const std::string &other) {...}
// 子结构的各个字段罗列
//
// 4、表结构:class XXX {...};
// 包括:
// 构造:XXX() {...}
// 拷贝构造:XXX(const XXX &other) {...}
// 赋值:XXX& operator = (const XXX &other) {...}
// 表结构的各个字段罗列
//
// 5、表结构对应的管理器类:class XXXTable {...};
// 包括:
// 构造:XXXTable();
// 析构:~XXXTable();
// 获取表格数据:Get
// 对于3个主键的,则生成:
// const XXX* Get(k1, k2, k3){...}
// const std::map<k3type, XXX>* Get(k1, k2){...}
// const std::map<k2type, std::map<k3type, XXX> >* Get(k1){...}
// const std::map<k1type, std::map<k2type, std::map<k3type, XXX> > >& Get(){...}
// 对于2个主键的则生成:
// const XXX* Get(k1, k2);
// const std::map<k2type, XXX>* Get(k1);
// const std::map<k1type, std::map<k2type, XXX> >& Get();
// 对于1个主键的则生成:
// const XXX* Get(k1);
// const std::map<k1type, XXX>& Get();
// 对于没有主键的,那么按照数组处理,生成:
// const XXX* Get(index);
// const std::vector<XXX>& Get();
// 表格加载:bool Load(const wchar_t* pFilePathName);
// 表格管理器的成员:
// 表格数据列表:m_XXXs,有主键的则为map,没有主键的则为vector
main函数涉及两个小功能:
一个是遍历输入参数,解析名命名空间,待处理的csv文件,代码生成路径
一个是迭代每一个待处理的csv文件名,传入CsvParse解析,然后对解析完的结果,逐个生成相应代码
完整的代码,比较长,这里就不贴了
给一个生成后的代码的示例吧:
还是前文的例子:
如上表格,namespace=common.table.csv
生成的头文件如下所示,
#ifndef __TestCsvLoader_h__
#define __TestCsvLoader_h__
#include <string>
#include <vector>
#include <map>
namespace common{
namespace table{
namespace csv{
class Test
{
public:
class Struct
{
public:
Struct();
Struct(const Struct &other);
Struct& operator = (const Struct &other);
Struct& operator = (const std::string &other);
unsigned int m_StructId;
int m_StructNum;
float m_StructFloat;
std::string m_StruceName;
};
class StructList
{
public:
StructList();
StructList(const StructList &other);
StructList& operator = (const StructList &other);
StructList& operator = (const std::string &other);
unsigned int m_StructId;
int m_StructNum;
float m_StructFloat;
std::string m_StruceName;
};
Test();
Test(const Test &other);
Test& operator = (const Test &other);
unsigned int m_Id1; //测试主键1
int m_Id2; //测试主键2
unsigned int m_Id3; //测试主键3
std::string m_Str; //测试字符串
std::vector<unsigned int> m_UInts; //测试数字列表
float m_Float; //测试数字
Struct m_Struct; //测试子结构
std::vector<StructList> m_StructLists; //测试子结构列表
};
class TestCsvLoader
{
public:
TestCsvLoader()
{
}
~TestCsvLoader()
{
}
const Test* Get(unsigned int id1, int id2, unsigned int id3) const;
const std::map<unsigned int, Test>* Get(unsigned int id1, int id2) const;
const std::map<int, std::map<unsigned int, Test> >* Get(unsigned int id1) const;
const std::map<unsigned int, std::map<int, std::map<unsigned int, Test> > >& Get() const;
bool LoadFile(const char* szPath);
bool ReloadFile(const char* szPath);
private:
std::map<unsigned int, std::map<int, std::map<unsigned int, Test> > > m_Tests;
};
}
}
}
#endif
放上完整工程的下载链接
工程中的boost库的路径为作者本机的路径,VS编译时请选择Release,重新配置boost库的include路径和lib路径