看到這個題目可能會有人問什麼是CSV表格呢,在我做這個實現之前我也是沒有聽說過CSV表格,更不知道他有什麼用了。CSV是一種Excel表格的導出格式,在Excel表格的菜單欄中點擊文件->另存為會彈出一個文件夾浏覽窗口,在下拉框中可以選擇保存格式,其中有一個就是.CSV(逗號分隔符)選項。
CSV表格的規則:
1 開頭是不留空,以行為單位。
2 可含或不含列名,含列名則居文件第一行。
3 一行數據不垮行,無空行。
4 以半角逗號(即,)作分隔符,列為空也要表達其存在。
5 列內容如存在半角逗號(即,)則用半角引號(即"")將該字段值包含起來。
6 列內容如存在半角引號(即")則應替換成半角雙引號("")轉義,並用半角引號(即"")將該字段值包含起來。
7 文件讀寫時引號,逗號操作規則互逆。
8 內碼格式不限,可為 ASCII、Unicode 或者其他。
9 不支持特殊字符
CSV表格的用途:
CSV表格也相當於關系數據庫中的二維表,但是他並不支持SQL語句的查詢,只能通過一些手段將表格內容存入合適的數據結構中便於查詢。如果結構選擇的合適,那麼查詢起來還是很方便的,但是他不利於表格中數據的更新,更新就好比是文件的操作一樣,各種文件指針游來游去,很繁瑣。如果程序中要使用外部數據,數據又不多不值當去用數據庫的話,就可以考慮使用CSV表格存儲外部數據,每當程序執行時可以從外部讀取數據進入內存,避免了程序因數據的改動而重新編譯。
使用C++讀取CSV表格的大致思路:
1:以打開文件的方式打開CSV表格,通過文件指針的各種游走,劃分出每一行,將一行數據存到一個string對象中。循環讀取文件中的每一行,文件讀取完畢後悔生成一個map<int, string>這樣的對象,int表示行號,string表示這一行中的所有字符。使用到的函數是strchr
2:再對每一行進行分割,分割的標准就是逗號。這樣對每一行來說會被分割出cols個string對象,把這些對象也存儲在一個map<int, string>對象中,這裡的int表示從左到右的列號,從1數起。這樣每一行數據會生成一個
map<int, string>對象,處理完一個文本會生成一個map<int, map<int, stirng>>對象。利用一個二重的映射關系將表格中的每一個字段全部有規律的存儲起來。
下面是具體的實現代碼:
[cpp]
#pragma once
//#include "stringparser.h"
#include <assert.h>
#include <map>
#include <vector>
#include <string>
#include "SkillRecord.h"
using namespace std;
typedef unsigned long u32;
class CppCSV
{
private:
map<u32, map<u32, string>> m_stringMap;
string m_CSVName;
public:
CppCSV(){}
CppCSV(const char *path)
{
assert(LoadCSV(path));
}
~CppCSV(){}
bool LoadCSV(const char *path);
bool SaveCSV(const char *path = NULL);
bool GetIntValue(u32 uiRow, u32 uiCol, int &riValue);
bool GetFloatValue(u32 uiRow, u32 uiCol, float &rfValue);
string* GetStringValue(u32 uiRow, u32 uiCol);
int GetParamFromString(string str, vector<string> &stringVec, char delim = ',');
map<u32, map<u32, string>>& GetCSVMap()
{
return m_stringMap;
}
void GetSkillRecordMapTable(map<int, SkillRecord> &sSkillMapTable);
};
#pragma once
//#include "stringparser.h"
#include <assert.h>
#include <map>
#include <vector>
#include <string>
#include "SkillRecord.h"
using namespace std;
typedef unsigned long u32;
class CppCSV
{
private:
map<u32, map<u32, string>> m_stringMap;
string m_CSVName;
public:
CppCSV(){}
CppCSV(const char *path)
{
assert(LoadCSV(path));
}
~CppCSV(){}
bool LoadCSV(const char *path);
bool SaveCSV(const char *path = NULL);
bool GetIntValue(u32 uiRow, u32 uiCol, int &riValue);
bool GetFloatValue(u32 uiRow, u32 uiCol, float &rfValue);
string* GetStringValue(u32 uiRow, u32 uiCol);
int GetParamFromString(string str, vector<string> &stringVec, char delim = ',');
map<u32, map<u32, string>>& GetCSVMap()
{
return m_stringMap;
}
void GetSkillRecordMapTable(map<int, SkillRecord> &sSkillMapTable);
};
[cpp]
#include "CppCSV.h"
#include <stdio.h>
//#include "stringparser.h"
bool CppCSV::LoadCSV(const char *path)
{
FILE *pFile = fopen(path, "r");
if (pFile)
{
fseek(pFile, 0, SEEK_END);
u32 uSize = ftell(pFile);
rewind(pFile);
char *fileBuffer = new char[uSize];
fread(fileBuffer, 1, uSize, pFile);
map<u32, string> stringMap;
u32 uiIndex = 1;
char *pBegin = fileBuffer;
char *pEnd = strchr(pBegin, '\n');
pBegin = pEnd + 1;
pEnd = strchr(pBegin, '\n');
while (pEnd)
{
string strTemp;
strTemp.insert(0, pBegin, pEnd-pBegin);
assert(!strTemp.empty());
stringMap[uiIndex++] = strTemp;
pBegin = pEnd + 1;
pEnd = strchr(pBegin, '\n');
}
delete []fileBuffer;
fileBuffer = NULL;
pBegin = NULL;
pEnd = NULL;
map<u32, string>::iterator iter = stringMap.begin();
for (; iter != stringMap.end(); ++iter)
{
vector<string> stringVec;
map<u32, string> stringMapTemp;
assert(GetParamFromString(iter->second, stringVec) > 0);
vector<string>::size_type idx = 0;
for (; idx != stringVec.size(); ++idx)
{
stringMapTemp[idx + 1] = stringVec[idx];
}
m_stringMap[iter->first] = stringMapTemp;
}
fclose(pFile);
m_CSVName = path;
return true;
}
else
{
return false;
}
}
bool CppCSV::SaveCSV(const char *path /* = NULL */)
{
if (path != NULL)
{
m_CSVName = path;
}
FILE *pFile = fopen(m_CSVName.c_str(), "w");
if (pFile)
{
map<u32, map<u32, string>>::iterator iter = m_stringMap.begin();
for (; iter != m_stringMap.end(); ++iter)
{
map<u32, string> &rStringMap = iter->second;
map<u32, string>::iterator it = rStringMap.begin();
for (; it != rStringMap.end(); ++it)
{
string strTemp = it->second;
strTemp += ',';
fwrite(strTemp.c_str(), 1, 1, pFile);
}
char delim = '\n';
fwrite(&delim, 1, 1, pFile);
}
fclose(pFile);
return true;
}
else
{
return false;
}
}
bool CppCSV::GetIntValue(u32 uiRow, u32 uiCol, int &riValue)
{
string *pStr = GetStringValue(uiRow, uiCol);
if (pStr)
{
riValue = atoi(pStr->c_str());
return true;
}
else
{
return false;
}
}
bool CppCSV::GetFloatValue(u32 uiRow, u32 uiCol, float &rfValue)
{
string *pStr = GetStringValue(uiRow, uiCol);
if (pStr)
{
rfValue = atof(pStr->c_str());
return true;
}
else
{
return false;
}
}
string* CppCSV::GetStringValue(u32 uiRow, u32 uiCol)
{
map<u32, map<u32, string>>::iterator iter = m_stringMap.find(uiRow);
if (iter != m_stringMap.end())
{
map<u32, string> &rStrMap = iter->second;
map<u32, string>::iterator it = rStrMap.find(uiCol);
if (it != rStrMap.end())
{
return &(it->second);
}
else
{
return NULL;
}
}
else
{
return NULL;
}
}
//用於分割字符串,將CSV表格中的一行按照規則解析成一組字符串,存儲在一個vector中
//根據CSV表格中所存儲的數據的不同,重載各函數
int CppCSV::GetParamFromString(string str, vector<string> &stringVec, char delim)
{
char *token = strtok(const_cast<char *>(str.c_str()), &delim);
while (token)
{
string strTemp = token;
stringVec.push_back(strTemp);
token = strtok(NULL, &delim);
}
return stringVec.size();
}
void CppCSV::GetSkillRecordMapTable(map<int, SkillRecord> &sSkillMapTable)
{
map<u32, map<u32, string>>::iterator iter = m_stringMap.begin();
for (; iter != m_stringMap.end(); ++iter)
{
map<u32, string> strmap = iter->second;
SkillRecord skillTemp;
skillTemp.SetID(atoi(strmap[1].c_str()));
skillTemp.SetPath(strmap[2]);
skillTemp.SetName(strmap[3]);
skillTemp.SetHurt(atoi(strmap[4].c_str()));
skillTemp.SetPlayTime(atoi(strmap[5].c_str()));
sSkillMapTable[skillTemp.GetID()] = skillTemp;
}
}
#include "CppCSV.h"
#include <stdio.h>
//#include "stringparser.h"
bool CppCSV::LoadCSV(const char *path)
{
FILE *pFile = fopen(path, "r");
if (pFile)
{
fseek(pFile, 0, SEEK_END);
u32 uSize = ftell(pFile);
rewind(pFile);
char *fileBuffer = new char[uSize];
fread(fileBuffer, 1, uSize, pFile);
map<u32, string> stringMap;
u32 uiIndex = 1;
char *pBegin = fileBuffer;
char *pEnd = strchr(pBegin, '\n');
pBegin = pEnd + 1;
pEnd = strchr(pBegin, '\n');
while (pEnd)
{
string strTemp;
strTemp.insert(0, pBegin, pEnd-pBegin);
assert(!strTemp.empty());
stringMap[uiIndex++] = strTemp;
pBegin = pEnd + 1;
pEnd = strchr(pBegin, '\n');
}
delete []fileBuffer;
fileBuffer = NULL;
pBegin = NULL;
pEnd = NULL;
map<u32, string>::iterator iter = stringMap.begin();
for (; iter != stringMap.end(); ++iter)
{
vector<string> stringVec;
map<u32, string> stringMapTemp;
assert(GetParamFromString(iter->second, stringVec) > 0);
vector<string>::size_type idx = 0;
for (; idx != stringVec.size(); ++idx)
{
stringMapTemp[idx + 1] = stringVec[idx];
}
m_stringMap[iter->first] = stringMapTemp;
}
fclose(pFile);
m_CSVName = path;
return true;
}
else
{
return false;
}
}
bool CppCSV::SaveCSV(const char *path /* = NULL */)
{
if (path != NULL)
{
m_CSVName = path;
}
FILE *pFile = fopen(m_CSVName.c_str(), "w");
if (pFile)
{
map<u32, map<u32, string>>::iterator iter = m_stringMap.begin();
for (; iter != m_stringMap.end(); ++iter)
{
map<u32, string> &rStringMap = iter->second;
map<u32, string>::iterator it = rStringMap.begin();
for (; it != rStringMap.end(); ++it)
{
string strTemp = it->second;
strTemp += ',';
fwrite(strTemp.c_str(), 1, 1, pFile);
}
char delim = '\n';
fwrite(&delim, 1, 1, pFile);
}
fclose(pFile);
return true;
}
else
{
return false;
}
}
bool CppCSV::GetIntValue(u32 uiRow, u32 uiCol, int &riValue)
{
string *pStr = GetStringValue(uiRow, uiCol);
if (pStr)
{
riValue = atoi(pStr->c_str());
return true;
}
else
{
return false;
}
}
bool CppCSV::GetFloatValue(u32 uiRow, u32 uiCol, float &rfValue)
{
string *pStr = GetStringValue(uiRow, uiCol);
if (pStr)
{
rfValue = atof(pStr->c_str());
return true;
}
else
{
return false;
}
}
string* CppCSV::GetStringValue(u32 uiRow, u32 uiCol)
{
map<u32, map<u32, string>>::iterator iter = m_stringMap.find(uiRow);
if (iter != m_stringMap.end())
{
map<u32, string> &rStrMap = iter->second;
map<u32, string>::iterator it = rStrMap.find(uiCol);
if (it != rStrMap.end())
{
return &(it->second);
}
else
{
return NULL;
}
}
else
{
return NULL;
}
}
//用於分割字符串,將CSV表格中的一行按照規則解析成一組字符串,存儲在一個vector中
//根據CSV表格中所存儲的數據的不同,重載各函數
int CppCSV::GetParamFromString(string str, vector<string> &stringVec, char delim)
{
char *token = strtok(const_cast<char *>(str.c_str()), &delim);
while (token)
{
string strTemp = token;
stringVec.push_back(strTemp);
token = strtok(NULL, &delim);
}
return stringVec.size();
}
void CppCSV::GetSkillRecordMapTable(map<int, SkillRecord> &sSkillMapTable)
{
map<u32, map<u32, string>>::iterator iter = m_stringMap.begin();
for (; iter != m_stringMap.end(); ++iter)
{
map<u32, string> strmap = iter->second;
SkillRecord skillTemp;
skillTemp.SetID(atoi(strmap[1].c_str()));
skillTemp.SetPath(strmap[2]);
skillTemp.SetName(strmap[3]);
skillTemp.SetHurt(atoi(strmap[4].c_str()));
skillTemp.SetPlayTime(atoi(strmap[5].c_str()));
sSkillMapTable[skillTemp.GetID()] = skillTemp;
}
}測試代碼:
[cpp]
CppCSV cs("skill.csv");
map<u32, map<u32, string>> stringMap = cs.GetCSVMap();
map<u32, map<u32, string>>::iterator iter = stringMap.begin();
for (; iter != stringMap.end(); ++iter)
{
map<u32, string> strmap = iter->second;
map<u32, string>::iterator it = strmap.begin();
for (; it != strmap.end(); ++it)
{
cout<<it->second<<" ";
}
cout<<endl;
}
CppCSV cs("skill.csv");
map<u32, map<u32, string>> stringMap = cs.GetCSVMap();
map<u32, map<u32, string>>::iterator iter = stringMap.begin();
for (; iter != stringMap.end(); ++iter)
{
map<u32, string> strmap = iter->second;
map<u32, string>::iterator it = strmap.begin();
for (; it != strmap.end(); ++it)
{
cout<<it->second<<" ";
}
cout<<endl;
}
由於從文件中讀取出來的內容都是存儲在string對象中,如果文件呢存儲的是一定意義的數據,需要進行轉換了。