配置文件内容:
root
{
num{-1.2 32}
human{man{boy},femal{girl}} #has other?
#have many
week{Sun. Mon. Tues. Wed. Thrus. Friday St.}
"end of 'root'"{'are "you" sure'}
}
other{}
读取它类实现:
/************************************************************************/
只读不写,只支持字符串类型
类内部以树状存储配置项,空串被解释为指代根节点
支持配置项嵌套定义, 行注释
全局配置项err_code和err_text用于描述配置文件解析错误
空格解释为间隔符, 区分大小写
单双引号配对标识字符串
暂时不支持转义字符
不要求必须存在最末空行
将来要兼容常见书写格式
/************************************************************************/
// 应该先预读分析一遍,防止递归解析时陷入死循环??
// 而且将文件整体放入缓冲区更容易管理内存!!
// 但那样就不方便支持实现动态配置项了(就是违反只读不写原则)
class CCfgNode
{
public:
CCfgNode()
{
};
CCfgNode(string const &name)
{
this->name = name;
};
string name;
vector<CCfgNode> value;
};
class CCfgFile
{
public:
CCfgFile();
virtual ~CCfgFile();
void setVal(string const &sID, string const &sVal, CCfgNode *pNode=NULL); // 在指定节点下创建子孙节点
void load(string const &sFn);
void dump(FILE *pFile=NULL, CCfgNode *pNode=NULL, int level=0);
char const *getVal(string const &sUP, string const &sID);
int getVal(string const &sID, vector<char const *> &vSet);
private:
void nest_parse(CCfgNode &node, string const &sEnder); // 暂时仅支持花括号这样的简单的层次分界符
CCfgNode *nest_scout(CCfgNode &node, string const &sID);
CCfgNode root_;
FILE *pfile_; // 保存递归解析中的当前文件状态
};
//
// Construction/Destruction
//
CCfgFile::CCfgFile() : pfile_(NULL)
{
}
CCfgFile::~CCfgFile()
{
}
// 更新或增加配置项
inline void CCfgFile::setVal(string const &sID, string const &sVal, CCfgNode *pNode/*=NULL*/)
{
if (pNode == NULL) pNode = &root_;
int pos=0;
for (pos=0; pos<pNode->value.size(); ++pos)
{
if (pNode->value[pos].name == sID) break;
}
if (pos == pNode->value.size())
{
pNode->value.push_back(CCfgNode(sID));
pNode->value.back().value.push_back(CCfgNode(sVal));
}
else
{
pNode = &pNode->value[pos];
for (pos=0; pos<pNode->value.size(); ++pos)
{
if (pNode->value[pos].name == sVal) break;
}
if (pos == pNode->value.size())
{
pNode->value.push_back(CCfgNode(sVal));
}
else
{
pNode->value[pos].name = sVal;
}
}
}
void CCfgFile::nest_parse(CCfgNode &node, string const &sEnder)
{
string sName;
int ch;
do
{
ch = fgetc(pfile_);
if (ch=='_' || ch=='.' || ch=='-' || isalnum(ch)) // 需要封装对标志符的识别
{
sName += ch;
}
else
{
if (!sName.empty())
{
node.value.push_back(CCfgNode(sName));
sName.erase();
}
switch (ch)
{
case '}': return;
case '{':
{
if (node.value.empty())
{
setVal("err_code", "-2");
setVal("err_text", "lack of identifier before '{'");
return;
}
nest_parse(node.value.back(), "");
break;
}
case '#':
{
do ch = fgetc(pfile_); while (ch!='/n' && ch!=EOF);
break;
}
case '`':
case '/'':
case '/"':
{
char endofch = ch;
ch=getc(pfile_);
while (ch!=endofch && ch!=EOF)
{
if (ch=='/r' || ch=='/n')
{
setVal("err_code", "-3");
setVal("err_text", "string has been break");
return;
}
sName += ch;
ch=getc(pfile_);
}
node.value.push_back(CCfgNode(sName));
sName.erase();
break;
}
default: break;
}
}
}
while (ch != EOF);
}
void CCfgFile::load(string const &sFn)
{
pfile_ = fopen(sFn.c_str(), "rb");
if (pfile_ == NULL)
{
setVal("err_code", "-1");
setVal("err_text", "open file fail");
return;
}
vector<CCfgNode>().swap(root_.value); // 彻底清空根节点
nest_parse(root_, "");
fclose(pfile_);
pfile_ = NULL;
}
void CCfgFile::dump(FILE *pFile/*=NULL*/, CCfgNode *pNode/*=NULL*/, int level/*=0*/)
{
if (pNode == NULL) pNode = &root_;
if (pFile == NULL) pFile = stderr;
for (int i=0; i<pNode->value.size(); ++i)
{
fprintf(pFile, "%*s%s/n", level*4, " ", pNode->value[i].name.c_str());
dump(pFile, &pNode->value[i], level+1);
}
}
CCfgNode *CCfgFile::nest_scout(CCfgNode &node, string const &sID)
{
if (node.name == sID) return &node;
CCfgNode *pNode=NULL;
for (int i=0; i<node.value.size(); ++i)
{
pNode = nest_scout(node.value[i], sID);
if (pNode != NULL) break;
}
return pNode;
}
char const *CCfgFile::getVal(string const &sUP, string const &sID)
{
CCfgNode *pNode=nest_scout(root_, sUP);
if (pNode != NULL)
{
for (int i=0; i<pNode->value.size(); ++i)
{
if (pNode->value[i].name == sID)
{
pNode = &pNode->value[i];
if (!pNode->value.empty())
{
return pNode->value[0].name.c_str(); // 只取首条配置子项的值
}
}
}
}
return ""; // 如此返回值到上层调用就不能区分配置项空值与不存在这两种情形了
}
int CCfgFile::getVal(string const &sID, vector<char const *> &vSet)
{
CCfgNode *pNode=nest_scout(root_, sID);
if (pNode != NULL)
{
for (int i=0; i<pNode->value.size(); ++i)
{
vSet.push_back(pNode->value[i].name.c_str());
}
}
return pNode!=NULL ? pNode->value.size() : 0;
}