游戏存储或者配置经常使用到逗号分隔文件,如何实现解析呢?
下面直接上代码
1、头文件CSVParser.h
#pragma once
#include "cocos2d.h"
#include
#include
using namespace std;
USING_NS_CC;
namespace CSVParser {
// 每一行的记录
class Row
{
public:
Row() { }
~Row() { }
void push_back(const string& value) { m_values.push_back(value); }
void setHeader(const vector<string>* header) { m_header = header; }
public:
// 每行数据有多少字段
unsigned int size() const { return m_values.size(); }
// 运算符 [] 重载
string& operator[](unsigned int key)
{
if (key < size()) return m_values[key];
throw "can't return this value (doesn't exist)";
}
// 运算符 [] 重载
string& operator[](const string& key)
{
vector<string>::const_iterator it;
int pos = 0;
for (it = (*m_header).begin(); it != (*m_header).end(); it++) {
if (key == *it) return m_values[pos];
pos++;
}
throw "can't return this value (doesn't exist)";
}
private:
const vector<string>* m_header;
vector<string> m_values;
};
class Csv
{
public:
Csv(const string& filename);
~Csv();
// 解析csv文件
void Parse(const string& filename);
// 错误信息
const string& getErrorInfo() const { return m_strErrorInfo; }
// 获取列头字段
vector<string> getHeader() const { return m_header; }
// 获取总行数
unsigned int getRowCount() const { return m_content.size(); }
// 获取总列数
unsigned int getColumnCount() const { return m_header.size(); }
// 运算符 [] 重载
Row& operator[](unsigned int key);
private:
// 读取整个文件的数据
void Load(const string& filename, string& Data);
void setHeader();
private :
// 原始表格数据
vector
m_content; // 所有行的数据(包含列头)
vector<string> m_header; // 列头字段
// 错误信息
string m_strErrorInfo;
};
}
2、实现CSVParser.cpp
#include "CSVParser.h"
namespace CSVParser {
Csv::Csv(const string& filename) {
Parse(filename);
}
Csv::~Csv() {
}
void Csv::Load(const string& filename, string& Data)
{
ssize_t len = 0;
unsigned char *data = NULL;
auto strPath = FileUtils::getInstance()->fullPathForFilename(filename);
data = FileUtils::getInstance()->getFileData(strPath.c_str(),"r", &len);
Data.assign((char *)data, len);
free(data);
}
void Csv::Parse(const string& filename)
{
// 清除之前的数据
m_content.clear();
m_strErrorInfo.clear();
string text;
Load(filename, text);
if (text.size() == 0) {
return;
}
// 定义状态
enum StateType {
NewFieldStart, // 新字段开始
NonQuotesField, // 非引号字段
QuotesField, // 引号字段
FieldSeparator, // 字段分隔
QuoteInQuotesField, // 引号字段中的引号
RowSeparator, // 行分隔符字符1,回车
Error, // 语法错误
};
Row Fields = Row();
string strField;
// 设置初始状态
StateType State = NewFieldStart;
for (int i = 0, size = text.size(); i < size; ++i) {
const char& ch = text[i];
switch (State) {
case NewFieldStart: { // 新字段开始
if (ch == '"') {
State = QuotesField;
}
else if (ch == ',') {
Fields.push_back("");
State = FieldSeparator;
}
else if (ch == '\r' || ch == '\n') {
m_strErrorInfo = "语法错误:有空行";
State = Error;
}
else {
strField.push_back(ch);
State = NonQuotesField;
}
}
break;
case NonQuotesField: { // 非引号字段
if (ch == ',') {
Fields.push_back(strField);
strField.clear();
State = FieldSeparator;
}
else if (ch == '\r') {
Fields.push_back(strField);
State = RowSeparator;
}
else {
strField.push_back(ch);
}
}
break;
case QuotesField: { // 引号字段
if (ch == '"') {
State = QuoteInQuotesField;
}
else {
strField.push_back(ch);
}
}
break;
case FieldSeparator: { // 字段分隔
if (ch == ',') {
Fields.push_back("");
}
else if (ch == '"') {
strField.clear();
State = QuotesField;
}
else if (ch == '\r') {
Fields.push_back("");
State = RowSeparator;
}
else {
strField.push_back(ch);
State = NonQuotesField;
}
}
break;
case QuoteInQuotesField: { // 引号字段中的引号
if (ch == ',') {
// 引号字段闭合
Fields.push_back(strField);
strField.clear();
State = FieldSeparator;
}
else if (ch == '\r') {
// 引号字段闭合
Fields.push_back(strField);
State = RowSeparator;
}
else if (ch == '"') {
// 转义
strField.push_back(ch);
State = QuotesField;
}
else {
m_strErrorInfo = "语法错误: 转义字符 \" 不能完成转义 或 引号字段结尾引号没有紧贴字段分隔符";
State = Error;
}
}
break;
case RowSeparator: { // 行分隔符字符1,回车
if (ch == '\n') {
m_content.push_back(Fields);
Fields = Row(); // Fields.clear();
strField.clear();
State = NewFieldStart;
}
else {
m_strErrorInfo = "语法错误: 行分隔用了回车 \\r。但未使用回车换行 \\r\\n ";
State = Error;
}
}
break;
case Error: { // 语法错误
return;
}
break;
default: break;
}
}
// end for
switch (State) {
case NewFieldStart: {
// Excel导出的CSV每行都以/r/n结尾。包括最后一行
}
break;
case NonQuotesField: {
Fields.push_back(strField);
m_content.push_back(Fields);
}
break;
case QuotesField: {
m_strErrorInfo = "语法错误: 引号字段未闭合";
}
break;
case FieldSeparator: {
Fields.push_back("");
m_content.push_back(Fields);
}
break;
case QuoteInQuotesField: {
Fields.push_back(strField);
m_content.push_back(Fields);
}
break;
case RowSeparator: {
}
break;
case Error: {
}
break;
default: break;
}
setHeader();
}
void Csv::setHeader()
{
m_header.clear();
for (unsigned int i = 0; i < m_content[0].size(); i++) {
m_header.push_back(m_content[0][i]);
}
for (unsigned int i = 0; i < m_content.size(); i++) {
m_content[i].setHeader(&m_header);
}
}
Row& Csv::operator[](unsigned int key)
{
if (key < m_content.size()) return m_content[key];
throw "can't return this row (doesn't exist)";
}
}
3、下载地址
CSVParser