读取配置文件csv的接口封装类。
csv配置文件既可以在excel中方便编辑,又体积较小,分析速度较快,所以是作为游戏等软件配置文件的较好的选择。
由于csv配置文件没有较好的c++库的支持(目前没见到),可以根据自己的需求编写读取接口库。
使用规则是第一行是各个字段的字段标注,后面的各行是具体的数值。
由于csv比较方便是在windows的excel下编辑,一般是gbk编码的。而服务器程序一般是utf8编码的。根据需求,可以对中文字符串类型的变量进行转码。
代码如下:
#ifndef _CSVFILE_H_
#define _CSVFILE_H_
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <iostream>
#include <assert.h>
#include <algorithm>
#include "CodeConvert.h"
#include "assert/mAssert.h"
//该类为读取csv文件接口
//根据字段名称来找到所对应的值
class CSVFile
{
public:
CSVFile()
{
m_pContext = NULL;//文件内容指针
m_pFile = NULL;//文件指针
m_pLinePtr = NULL;//行头指针
}
~CSVFile()
{
if (NULL != m_pContext)
{
delete []m_pContext;
m_pContext = NULL;
}
m_pLinePtr = NULL;
m_pFile = NULL;
}
<span style="white-space:pre"> </span>//读取文件内容到缓存
inline bool Open(bool bIsRead, const char* strPath, const char* strFilename)
{
m_nFileState = FILE_STATE_NULL;
m_sFullFileName = strPath;
m_sFullFileName += strFilename;
m_pFile = fopen(m_sFullFileName.c_str(), "rb");
if(!m_pFile)
{
return false;
}
fseek(m_pFile, 0, SEEK_END);
long size = ftell(m_pFile);
fseek(m_pFile, 0, SEEK_SET);
char *buffer = new char[size + 1];
size_t nRead = fread(buffer, 1, size, m_pFile);
fclose(m_pFile);
if((long)nRead != size)
return false;
buffer[nRead] = 0;
<span style="white-space:pre"> </span>//如果程序编码是 utf8且 cvs配置文件编码是gbk则需要转码为utf8
#if (UTF8 == PROGRAM_CODE) && (GBK == CSV_CODE)
m_pContext = new char[size*2 + 1];
int nConvertedSize = gbk2utf8(m_pContext,size*2,buffer);
m_pContext[nConvertedSize] = '\0';
delete []buffer;
#else
m_pContext = buffer;
#endif
m_pLinePtr = m_pContext;
ReadCSVHead();//读取csv配置文件的第一行(也是配置的各个字段名称)
if (bIsRead)
{
m_nFileState = FILE_STATE_READ;//开始读取文件数据
}
return true;
}
// 读取csv文件中的一行数据
inline bool CSVReadNextRow()
{
if (m_nFileState != FILE_STATE_READ)
return false;
const char* sLine = m_pLinePtr; //行头
const char* sLineEnd = strchr(sLine, '\n'); //行尾
if (!sLineEnd)
{
return false;
}
m_pLinePtr = sLineEnd + 1;
if (sLineEnd[-1] == '\r')//去掉windows 文件的换行符的'\r'
sLineEnd--;
//一行内容空
if(0 == (sLineEnd - sLine))
return false;
RowParse(sLine, int(sLineEnd - sLine), m_CSVCurRow);//读取该行数据
return true;
}
template<class T>
bool CSVRead(const char* strFieldName, T& target)//根据字段名称,读取该行与之相对应的值
{
std::string data;
if (m_nFileState != FILE_STATE_READ)
{
return false;
}
int n = FindField(strFieldName);//获取该字段名称所在列数
if (n == -1 || n >= (int)m_CSVCurRow.size())
{
return false;
}
data = m_CSVCurRow[n];//根据字段名称所在列数,找到该值
convert(target,data);
return true;
}
inline bool CSVRead(const char* strFieldName, char* target,size_t len)//根据字段名称,读取该行中之相对应的值
{
std::string data;
if (m_nFileState != FILE_STATE_READ)
{
return false;
}
int n = FindField(strFieldName);//获取该字段名称所在列数
if (n == -1 || n >= (int)m_CSVCurRow.size())
{
return false;
}
data = m_CSVCurRow[n];//根据字段名称所在列数,找到该值
convert(target,len,data);
return true;
}
template<class T>
bool CSVWrite(const char* strFieldName, T data)//写接口,目前没有使用
{
if (m_nFileState != FILE_STATE_WRITE)
{
return false;
}
int n = FindField(strFieldName);//找到字段名称所在的列数
if (n == -1)
{
return false;
}
std::stringstream ss;
ss << data;
m_CSVCurRow[n] = ss.str();
return true;
}
//获取总行数
inline size_t GetTotalRow()
{
const char* sLine = m_pContext;
const char* sLineEnd = strchr(sLine, '\n');
size_t nCount = 0;
while (sLineEnd)
{
sLineEnd = strchr(sLineEnd, '\n');
if(sLineEnd)
{
nCount++;
sLineEnd++;
}
else
break;
}
return nCount-1;
}
//数据类型转换
//转化为 char
inline void convert(char& target,const std::string& str)
{
if (str.empty())
{
target = (char)0;
}
else
{
target = str.c_str()[0];
if (target>='0' && target <='9')
{
target = target - '0';
}
}
}
//转化为unsigned char
inline void convert(unsigned char& target,const std::string& str)
{
if (str.empty())
{
target = (unsigned char)0;
}
else
{
target = (unsigned char)str.c_str()[0];
if (target>='0' && target <='9')
{
target = target - '0';
}
}
}
//转化为 short
inline void convert(short& target,const std::string& str)
{
if (str.empty())
{
target = (short)0;
}
else
{
target = (short)atoi(str.c_str());
}
}
//转化为unsigned short
inline void convert(unsigned short& target,const std::string& str)
{
if (str.empty())
{
target = (unsigned short)0;
}
else
{
target = (unsigned short)atoi(str.c_str());
}
}
//转化为 int
inline void convert(int& target,const std::string& str)
{
if (str.empty())
{
target = (int)0;
}
else
{
target = atoi(str.c_str());
}
}
//转化为 int
inline void convert(unsigned int& target,const std::string& str)
{
if (str.empty())
{
target = (unsigned int)0;
}
else
{
target = (unsigned int)atol(str.c_str());
}
}
//转化为 long(要64位的话,建议不使用long 而用long long,long在64位win下是4字节,在64位linux下是8字节)
inline void convert(long& target,const std::string& str)
{
if (str.empty())
{
target = (long)0;
}
else
{
target = (long)atol(str.c_str());
}
}
//转化为 unsigned long
inline void convert(unsigned long& target,const std::string& str)
{
if (str.empty())
{
target = (unsigned long)0;
}
else
{
target = (unsigned long)strtoul(str.c_str(),NULL,0);
//char *endptr = NULL;
}
}
//转化为 long double
inline void convert(long double& target,const std::string& str)
{
if (str.empty())
{
target = (long double)0;
}
else
{
target = (long double)atoll(str.c_str());
}
}
//转化为 long long
inline void convert(long long& target,const std::string& str)
{
if (str.empty())
{
target = (long long)0;
}
else
{
target = (long long)atoll(str.c_str());
}
}
//转化为 float
inline void convert(float& target,const std::string& str)
{
if (str.empty())
{
target = 0;
}
else
{
char *endptr = NULL;
target = (float)strtod(str.c_str(),&endptr);
if (endptr && endptr[0])
{
target = 0;
std::cout << "config data error:" <<endptr << std::endl;
}
}
}
//转化为 double
inline void convert(double& target,const std::string& str)
{
if (str.empty())
{
target = 0;
}
else
{
char *endptr = NULL;
target = strtod(str.c_str(),&endptr);
if (endptr && endptr[0])
{
target = 0;
std::cout << "config data error:" <<endptr << std::endl;
}
}
}
//转化为 const char *
inline void convert(const char *target,size_t targetLen,const std::string& str)
{
if (str.empty())
{
bzero((char *)target,targetLen);
return;
}
#if (UTF8 == PROGRAM_CODE) && (GBK == CSV_CODE)
gbk2utf8(target,targetLen,str.c_str());
#else
strncpy((char *)target,str.c_str(),targetLen);
#endif
if (targetLen <= str.length())//最后字节置0
*(char *)(target + targetLen - 1) = 0;
}
//转化为 char *
inline void convert(char *target,size_t targetLen,const std::string& str)
{
if (str.empty())
{
bzero(target,targetLen);
return;
}
#if (UTF8 == PROGRAM_CODE) && (GBK == CSV_CODE)
gbk2utf8(target,(int)targetLen,str.c_str());//转换gbk编码到utf8编码
#else
strncpy(target,str.c_str(),targetLen);
if (targetLen <= str.length())
target[targetLen - 1] = 0;
#endif
}
//转化为 std::string
inline void convert(std::string& target,const std::string& str)
{
if (str.empty())
{
target.clear();
return;
}
#if (UTF8 == PROGRAM_CODE) && (GBK == CSV_CODE)
gbk2utf8(target,str.c_str());
#else
target = str;
#endif
}
//转化为 long long
void convert(long long& target,const std::string& str)
{
if (str.empty())
{
target = 0;
}
else
{
char *endptr = NULL;
target = (long long)strtod(str.c_str(),&endptr);
if (endptr && endptr[0])
{
target = 0;
std::cout << "config data error:" <<endptr << std::endl;
}
}
}
private:
//保存每一行内容的容器
typedef std::vector<std::string> ROWVEC;
//读取字段名词行并保存起来
inline void ReadCSVHead()
{
const char* sLine = m_pLinePtr; //行头
const char* sLineEnd = strchr(sLine, '\n'); //行尾
if (sLineEnd)
{
m_pLinePtr = sLineEnd + 1;
if (sLineEnd[-1] == '\r')
sLineEnd--;
}
else sLine = sLineEnd = "";
RowParse(sLine, int(sLineEnd - sLine), m_CSVHead);
}
//strRow是csv文件中的一行的起始位置
//nSize 一行的长度
//result 一行的各个字段的字符串队列
inline void RowParse(const char* strRow, int nSize, ROWVEC& result)
{
result.clear();
std::string strCurWord;
for (int i = 0; i < nSize; ++i)
{
char ch = strRow[i];
bool bIsAdd = true;
switch (ch)
{
case ',':
{
//一项结束
result.push_back(strCurWord);
strCurWord = "";
bIsAdd = false;
}
break;
case '"':
//去掉空格
case ' ':
case '\r'://去掉\r
case '\t'://去掉\t
{
bIsAdd = false;
}
break;
default:
break;
};
if (bIsAdd)//添加合法字符
strCurWord += ch;
}
result.push_back(strCurWord);
}
//删除字符串中的‘\r’字符
inline void delete_r(std::string &str)
{
while(1)
{
std::string::iterator it = std::find(str.begin(), str.end(), '\r');
if(it == str.end())
break;
str.erase(it);
}
}
//strRow 是程序中的字段
//return 字段列数
inline int FindField(const char* strRow)//获取字段名称对应的列数
{
#if (UTF8 == PROGRAM_CODE) && (GBK == CSV_CODE)
std::string tarStr;
gbk2utf8(tarStr,strRow);
#endif
if (m_nFileState == FILE_STATE_NULL)
return -1;
for (ROWVEC::iterator it = m_CSVHead.begin();it != m_CSVHead.end(); it++)
{
#if (UTF8 == PROGRAM_CODE) && (GBK == CSV_CODE)
if (*it == tarStr)
{
return int(it - m_CSVHead.begin());
}
#else
if (*it == strRow)
{
return int(it - m_CSVHead.begin());
}
#endif
}
printf("字段 ( %s) 没有找到,配置文件为(%s)\n",strRow,m_sFullFileName.c_str());
assert_fail("配置文件字段没有找到");
return 0;
}
enum FileState
{
FILE_STATE_NULL, //未初始化
FILE_STATE_READ, //文件可写
FILE_STATE_WRITE, //文件可读
};
FileState m_nFileState;//文件状态机
//std::fstream m_CSVFile;
FILE* m_pFile;
char* m_pContext;
const char* m_pLinePtr; //行记录指针
ROWVEC m_CSVHead; //字段名称行
ROWVEC m_CSVCurRow; //当前内容行
std::string m_sFullFileName;//文件名称全路径
};
#endif //_CSVFILE_H_