读取配置文件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_
时间: 2024-11-08 22:01:12