cocos2d-x开发: 一切应该从配置文件读取开始
想要做一款完整的游戏,应该从配置文件读取开始。cocos2d-x本身提供了UserDefault来操作xml格式的配置文件,准确的说配置这模块引擎开发者已经考虑到了.但是xml格式包含大量无关的格式信息,对于开发者直接使用编辑器操作也不是很友好.我期望的配置文件应该具备两个特点,第一:格式简单.这是我一贯的*nix风格的要求,我喜欢只管的#注释以及key-value的配置方式.可能唯一需要考虑的是这样简单的配置方式会不会因为业务扩展而显得功能不足.这一点是可以放心的,那些都是业务逻辑相关的,选择就会很多.至少现在不是应该考虑的范畴.第二:对开发者友好.什么叫做友好? 说白了,按照我的理解就是很容易可以定位配置信息的位置,容易修改,对编辑器依赖不高.我们随便用个notepad++/subl之类的文本编辑器就可以直接阅读和修改.
好了,基于上面的考虑,ini格式无疑是比较好的选择.我最终选用了ini格式作为配置文件首选.所以就写了一个c++类来解析和操作ini配置.代码如下:
#ifndef __xy_INICache_INCLUDE__
#define __xy_INICache_INCLUDE__
#include <map>
#include <vector>
#include <string>
namespace xy
{
class INICache
{
public:
INICache();
virtual ~INICache();
static INICache* createFromFile(const std::string& filePath);
static INICache* createFromStream(const std::stringstream& sstream);
bool loadFromFile(const std::string& filePath);
bool loadFromStream(const std::stringstream& sstream);
void flush(const std::string& filePath);
std::string getFilePath() const { return this->M_filePath_; }
bool isSectionExist(const std::string& section) const;
bool deleteSection(const std::string& section);
void getSectionNames(std::vector<std::string>& sectionVector);
void setString(const std::string& section, const std::string& key, const std::string& value);
std::string getString(const std::string& section, const std::string& key, const std::string& defaultValue);
void setInteger(const std::string& section, const std::string& key, int value);
int getInteger(const std::string& section, const std::string& key, int defaultValue);
void setDouble(const std::string& section, const std::string& key, double value, int pre);
double getDouble(const std::string& section, const std::string& key, double defaultValue);
void setBoolean(const std::string& section, const std::string& key, bool value);
bool getBoolean(const std::string& section, const std::string& key, bool defalutValue);
protected:
void trimString(std::string& buffer, const std::string& trim, bool isAll);
void parseLine(const std::string& buffer);
std::string parseSection(const std::string& buffer, const std::string& leftTag, const std::string& rightTag);
bool stringToBoolean(const std::string& buffer, bool defaultValue); typedef std::map<std::string, std::string> KVtype;
typedef std::map<std::string, KVtype> SKVtype; std::string M_filePath_;
std::string M_currentSection_;
SKVtype propsMap_;
};
}
#endif//__xy_INICache_INCLUDE__
实现部分的代码如下,如果需要看的,可以自行查看.
#include "INICache.h"
#include <fstream>
#include <sstream>
namespace xy
{
INICache::INICache()
{
}
INICache::~INICache()
{
if(!this->propsMap_.empty()) { this->propsMap_.clear(); }
}
INICache* INICache::createFromFile(const std::string& filePath)
{
INICache* pINICache = new INICache;
if(!pINICache->loadFromFile(filePath))
{
delete pINICache;
return NULL;
}
return pINICache;
}
bool INICache::loadFromFile(const std::string& filePath)
{
std::ifstream fin(filePath.c_str());
if(!fin.is_open()) { return false; } std::string lineBuffer;
while(!fin.eof())
{
std::getline(fin, lineBuffer);
this->trimString(lineBuffer, std::string(" "), true);
this->parseLine(lineBuffer);
}
fin.close();
this->M_filePath_ = filePath;
return true;
}
INICache* INICache::createFromStream(const std::stringstream& sstream)
{
INICache* pINICache = new INICache;
if(!pINICache->loadFromStream(sstream))
{
delete pINICache;
return NULL;
}
return pINICache;
}
bool INICache::loadFromStream(const std::stringstream& sstream)
{
std::string lineBuffer;
std::istringstream isstream(sstream.str());
while(!isstream.eof())
{
std::getline(isstream, lineBuffer);
this->trimString(lineBuffer, std::string(" "), true);
this->parseLine(lineBuffer);
}
return true;
}
void INICache::flush(const std::string& filePath)
{
std::ofstream fout(filePath.c_str());
if(!fout.is_open()) { return; }
std::string buffer;
SKVtype::iterator skv_iter;
KVtype::iterator kv_iter;
for(skv_iter = this->propsMap_.begin(); skv_iter != this->propsMap_.end(); ++skv_iter)
{
fout << std::endl;
fout << std::string("[") << skv_iter->first << std::string("]") << std::endl;
for(kv_iter = skv_iter->second.begin(); kv_iter != skv_iter->second.end(); ++kv_iter)
{
fout << kv_iter->first << std::string("=") << kv_iter->second << std::endl;
}
}
fout.close();
}
bool INICache::isSectionExist(const std::string& section) const
{ return (this->propsMap_.find(section) != this->propsMap_.end()); }
bool INICache::deleteSection(const std::string& section)
{
if(!section.empty()) { return false; }
SKVtype::iterator skv_iter = this->propsMap_.find(section);
if(skv_iter != this->propsMap_.end())
{
this->propsMap_.erase(skv_iter);
return true;
}
return false;
}
void INICache::getSectionNames(std::vector<std::string>& sectionVector)
{
if(!sectionVector.empty()) { sectionVector.clear(); }
SKVtype::iterator skv_iter = this->propsMap_.begin();
for(; skv_iter != this->propsMap_.end(); ++skv_iter)
{ sectionVector.push_back(skv_iter->first); }
}
void INICache::setString(const std::string& section, const std::string& key, const std::string& value)
{
if(!section.empty() && !key.empty())
{ this->propsMap_[section][key] = value; }
}
std::string INICache::getString(const std::string& section, const std::string& key, const std::string& defaultValue)
{
if(!section.empty() && !key.empty())
{
SKVtype::iterator skv_iter = this->propsMap_.find(section);
if(skv_iter != this->propsMap_.end())
{
KVtype::iterator kv_iter = skv_iter->second.find(key);
if(kv_iter != skv_iter->second.end())
{ return kv_iter->second; }
}
}
return defaultValue;
}
void INICache::setInteger(const std::string& section, const std::string& key, int value)
{
std::stringstream sstream;
sstream << value;
this->setString(section, key, sstream.str());
}
int INICache::getInteger(const std::string& section, const std::string& key, int defaultValue)
{
std::string tmp = this->getString(section, key, std::string(""));
std::stringstream sstream;
sstream << tmp;
sstream >> defaultValue;
return defaultValue;
}
void INICache::setDouble(const std::string& section, const std::string& key, double value, int pre)
{
std::stringstream sstream;
if(pre) { sstream.precision(pre); }
sstream << value;
this->setString(section, key, sstream.str());
}
double INICache::getDouble(const std::string& section, const std::string& key, double defaultValue)
{
std::string tmp = this->getString(section, key, std::string(""));
std::stringstream sstream;
if(!tmp.empty())
{
sstream << tmp;
sstream >> defaultValue;
}
return defaultValue;
}
void INICache::setBoolean(const std::string& section, const std::string& key, bool value)
{ this->setInteger(section, key, value ? :); }
bool INICache::getBoolean(const std::string& section, const std::string& key, bool defaultValue)
{
std::string tmp = this->getString(section, key, std::string(""));
if(!tmp.empty()) { return this->stringToBoolean(tmp, defaultValue); }
return defaultValue;
}
void INICache::trimString(std::string& buffer, const std::string& trim, bool isAll)
{
if(buffer.empty()) { return; }
while(buffer.find(trim) == )
{
buffer.erase(, trim.length());
if(!isAll) { break; }
}
while(!buffer.empty() && (buffer.rfind(trim) == (buffer.length() - trim.length())))
{
buffer.erase(buffer.length() - trim.length(), trim.length());
if(!isAll) { break; }
}
}
void INICache::parseLine(const std::string& buffer)
{
if(buffer.empty()) { return; }
switch (buffer[])
{
case '#':
case '%':
return;
case '[':
{
std::string section = this->parseSection(buffer, std::string("["), std::string("]"));
this->trimString(section, std::string(" "), true);
if(!section.empty()) { this->M_currentSection_ = section; }
}
return;
default:
{
if(buffer.find(std::string("=")) != std::string::npos && !this->M_currentSection_.empty())
{
std::string key = this->parseSection(buffer, std::string(""), std::string("="));
this->trimString(key, std::string(" "), true);
std::string value = this->parseSection(buffer, std::string("="), std::string(""));
this->trimString(value, std::string(" "), true);
if(!key.empty()) { this->propsMap_[this->M_currentSection_][key] = value; }
}
}
return;
}
}
std::string INICache::parseSection(const std::string& buffer, const std::string& leftTag, const std::string& rightTag)
{
std::string ret;
if(!buffer.empty())
{
std::string::size_type pos_begin = , pos_end = ;
if(!leftTag.empty())
{
pos_begin = buffer.find(leftTag);
if(pos_begin == std::string::npos) { return ret; }
pos_begin += leftTag.size();
} else { pos_begin = ; }
if(!rightTag.empty())
{
pos_end = buffer.find(rightTag, pos_begin);
if(pos_end == std::string::npos) { return ret; }
ret = buffer.substr(pos_begin, pos_end - pos_begin);
} else { ret = buffer.substr(pos_begin, std::string::npos); }
}
return ret;
}
bool INICache::stringToBoolean(const std::string& buffer, bool defaultValue)
{
if(buffer.empty()) { return defaultValue; }
std::stringstream sstream;
sstream << buffer;
int tmp = ;
sstream >> tmp;
return (buffer.compare(std::string("true"))== || tmp > );
}
}
INICache.cc
这里主要是说一下我做的这些接口的思路. 读取涉及到的get/set这些方法,是基本需求.提供了两个读取Ini buffer的方法,一个是loadFromFile(const std::string& filePath),这个接口主要是应对客户端从本地读取配置文件信息,而loadFromStream(const std::stringstream& sstream)接口则是作为预留,从buffer直接读取Ini配置,为什么会做这样一个接口的预留呢? 可以一起看一下getSectionNames这个接口方法,返回的是key值的集合.
我是这样考虑的,c++这部分的接口尽量的简单,实现基本的功能,尽量避开业务逻辑的操作.要什么,我就提供基本接口,除非是在lua那边实现效率不高,或者是复杂,才会考虑提供完整的功能.因为紧接着下面会做更新模块的功能,这部分至少会依赖读取配置中一条关于资源版本的信息.而从远程Http服务器下载资源的时候也还是需要做很多版本的检测处理,如果是增量更新的话,需要按照版本的先后顺序依次下载.这就是要对key值进行按照需求排序. 本来在c++这部分做很简单的,我只要用std::sort函数,提供一个compare函数就好了。可是这部分是和具体的更新业务逻辑关联的,所以我不应该在c++这部分实现,所以就只是提供给你获取集合的方法.然后自己去处理.
绑定这部分就没什么好说的了. getSectionNames loadFromStream createFromStream这三个函数接口需要skip掉,结合前面的更新需求,需要的话,另外绑定接口,传入lua callback,在C++这边调用getSectionNames接口就好了,具体细节在后面写到更新的时候自然就会写出来了.我写了一个测试:
---------------------------------------------------------------------
-- @Author 小岩
-- @Created on 2014-12-26 21:29
-- @Brief INI解析测试
---------------------------------------------------------------------
INICacheTestCase = class("INICacheTestCase", TestsCase)
-----------------------------------------------------------
-- 测试解析INI文件
function INICacheTestCase:run()
local iniCache = xy.INICache:createFromFile("src/Tests/INICache/INICache_conf.ini")
if iniCache == nil then
Logger.Error(" cannot get local conf file! ")
end
Logger.Info("load file succ!")
Logger.Info("%s", iniCache:getString("Section", "Key", "Failed"))
Logger.Info("%d", iniCache:getInteger("Section", "Integer", "-1"))
Logger.Info("%f", iniCache:getDouble("Section", "Double", "-1"))
local boolean = iniCache:getBoolean("Section", "Boolean", false)
if boolean == true then
Logger.Info("%s", "boolean == true")
else
Logger.Info("%s", "boolean == false")
end
end
---------------------------------------------------------------------
-- End Of Lua File
---------------------------------------------------------------------
需要注意的问题是,INICache读取文件后,将配置文件信息一直都是保存在map中的,所以不要在不同的地方对同一份配置文件做多次读取操作,这样的话,将配置再次持久化到设备的时候,配置信息就会错掉.所以最好是提供配置的单例操作方式.
cocos2d-x开发: 一切应该从配置文件读取开始的更多相关文章
- [spring源码学习]二、IOC源码——配置文件读取
一.环境准备 对于学习源码来讲,拿到一大堆的代码,脑袋里肯定是嗡嗡的,所以从代码实例进行跟踪调试未尝不是一种好的办法,此处,我们准备了一个小例子: package com.zjl; public cl ...
- C 构造一个 简单配置文件读取库
前言 最近看到这篇文章, json引擎性能对比报告 http://www.oschina.net/news/61942/cpp-json-compare?utm_source=tuicool 感觉技术 ...
- C# 配置文件读取与修改(转)
C# 配置文件读取与修改 配置文件在很多情况下都使用到, 配置文件分为两种 一种是应用程序的配置文件, 一种是web的配置文件. 两种配置文件最大的区别是web的配置文件更新之后会实时更新, 应用 ...
- Aooms_微服务基础开发平台实战_003_配置文件与简单的web环境搭建
一.前言 本篇文章介绍两个重点 (1) 工程核心配置文件application.yml (2) 如何在一个标准的的SpringCloud工程上构建起一个基本的web结构 二.配置文件applicati ...
- Java配置文件读取中文乱码问题
背景 这是我之前在做的用友服务对接开发,昨天领导拿给财务测试时告诉我有乱码,当时我第一想法是用友那边的编码格式有问题,因为还在做其他任务,我说等问一下用友他们用的什么编码格式我们这边改一下,然后今天早 ...
- ASP.Net Core 5.0 MVC 配置文件读取,Startup 类中ConfigureServices 方法、Configure 方法的使用
配置文件读取 1. 新建FirstController控制器 在appsettings文件内容替换成以下代码 { "Position": { "Title": ...
- VS2012中,C# 配置文件读取 + C#多个工程共享共有变量 + 整理using语句
(一) C# 配置文件读取 C#工程可以自动生成配置文件,以便整个工程可以使用设置的配置进行后续的处理工作. 1. 首先,右键工程文件-->Properties -->settings-- ...
- C# 配置文件读取与修改
C# 配置文件读取与修改 配置文件在很多情况下都使用到, 配置文件分为两种 一种是应用程序的配置文件, 一种是web的配置文件. 两种配置文件最大的区别是web的配置文件更新之后会实时更新, 应用 ...
- java Spring使用配置文件读取jdbc.properties
Spring使用配置文件读取jdbc.properties 在beans.xml中加入两个必须的bean [html]<bean id="propertyConfigurer" ...
随机推荐
- BZOJ1014 [JSOI2008]火星人
Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam, 我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 ...
- 初级篇html。
什么是html? 超文本标记语言,标准通用标记语言下的一个应用. “超文本”就是指页面内可以包含图片.链接,甚至音乐.程序等非文字元素. 超文本标记语言的结构包括“头”部分(英语:Head).和“主 ...
- wampserver 更改www目录
现在大家基本上开发php的有很大一部分都在用Wampserver,今天来讲讲怎么更改默认的www目录, 需要修改的文件有三个 apache2的配置文件 httpd.conf 和 Wampserver的 ...
- MYSQL LOGBIN 数据日志恢复数据库随笔
查看指定的二进制日志中的事件(MYSQL命令行) mysql> show binlog events in 'binlogfullpath'; 查看二进制日志中的事件(MYSQL命令行) mys ...
- python数据处理与机器学习
提纲 numpy: #genformtxt import numpy as np #genformtxtdata=np.genfromtxt("genfromtxtdata") # ...
- Attribute+Reflection,提高代码重用
这篇文章两个目的,一是开阔设计的思路,二是实例代码可以拿来就用. 设计的思路来源于<Effective c#>第一版Item 24: 优先使用声明式编程而不是命令式编程.特别的地方是,希望 ...
- javascript使用web proxy来实现ajax cross-domain通信
在现代浏览器中,都强加了对javacript代码的访问限制,比如一个页面的js无法向非同源的url实现ajax请求,获得数据.在这时,是浏览器端会报错: No 'Access-Control-Allo ...
- 导入自定义模块model
编写m2.py,脚本内容如下: #!/usr/bin/python # -*- coding: utf-8 -*- 'its a module test' __author__ = 'mm' impo ...
- 【转】outlook 2016 配置自动发现
今天一部门经理换了高大上的终端,办公软件安装的是office 2016,在配置outlook的时候,懵逼了,没有exchange 选项,使用自动发现配置的时候,电脑没有加域,各种尝试,一直过不去,哎, ...
- 在 Windows Server Container 中运行 Azure Storage Emulator(二):使用自定义的 SQL Server Instance
上一节,我们解决了 Azure Storage Emulator 自定义监听地址的问题,这远远不够,因为在我们 DEV/QA 环境有各自的 SQL Server Instance,我们需要将 ASE ...