by Laniakea.White
一 ini做配置
1.0 ini例子
[test1]
key11=val1
key12 = val2
[test2]
key21=val23
1.1 实现方式
只使用语言中最基本的字符串分割方式,或者使用语言自带的正则表达式,对配置内容进行分割,然后解析出来.
windows的话,好像有很古老的API,可以支持解析ini配置文件的。
##1.2 优势
1.通用好: 实现所依赖的功能,是语言中最基本的东西,这就意味着,你可以在十分底层的代码中使用,而不需要引入新的库。比如,在嵌入式或者单片机中都是可以使用。
我自己就是拿来做程序引导时的配置解析的。
1.3 劣势
- 不容易做复杂的配置:ini这种结构,要做复杂的配置不太容易,通常使用都是很简单的key-value的方式。比如,如果要做二级子节点,就不行了。
- 解析麻烦:还是最原始的分割,然后解析
- 容易出错:ini配置时的内容是无类型的,具体要解析成什么格式的数据,需要业务层关联,这样子万一配错了,进行类型转换时就容易出问题。
1.4 一个实现
这里给出一个网上别人的实现:https://blog.csdn.net/weixin_34210740/article/details/92897688
confile.h
/**
* INI配置文件管理函数库
* Ini file parse functions.
* By Hoverlees http://www.hoverlees.com me[at]hoverlees.com
*/
#ifndef _HOVERLEES_INI_CONFIG_H
#define _HOVERLEES_INI_CONFIG_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
//#include <unistd.h>
typedef struct _CONFIG_BTREE_NODE{
char* key;
void* data;
struct _CONFIG_BTREE_NODE* left;
struct _CONFIG_BTREE_NODE* right;
char mem[2];
}CONFIG_BTREE_NODE;
typedef struct _CONFIG_BTREE{
int numNodes;
CONFIG_BTREE_NODE* root;
}CONFIG_BTREE;
typedef CONFIG_BTREE INI_CONFIG;
typedef int (*CONFIG_BTREE_TRAVERSE_CB)(CONFIG_BTREE_NODE* node);
typedef int (*CONFIG_BTREE_SAVE_TRAVERSE_CB)(FILE* fp,CONFIG_BTREE_NODE* node);
/**
* ini内容解析函数,从字符串解析配置
* @param str 字符串
* @param slen 字符串长度,可以为0,如果为0,函数自动计算字符串长度
* @param isGBK 如果ini文件使用GBK字符集,设置成1,否则设置成0
* @return 成功返回INI_CONFIG指针,失败返回null
*/
INI_CONFIG* ini_config_create_from_string(unsigned char* str,int slen,int isGBK);
/**
* ini内容解析函数,从文件解析配置
* @param filename 配置文件名
* @param isGBK 如果ini文件使用GBK字符集,设置成1,否则设置成0
* @return 成功返回INI_CONFIG指针,失败返回null
*/
INI_CONFIG* ini_config_create_from_file(const char* filename,int isGBK);
/**
* 配置释放函数,释放所占用的内存及数据结构
* @param config 配置对象指针
* @return 成功返回1,失败返回0
*/
void ini_config_destroy(INI_CONFIG* config);
/**
* 获取配置整数值
* @param config 配置对象指针
* @param section 段名,没有段名时可以为NULL
* @param key 键名
* @param default_int 默认值
* @return 如果配置中有此键对应的值,返回该值,否则返回参数指定的默认值
*/
int ini_config_get_int(INI_CONFIG* config,const char* section,const char* key,int default_int);
/**
* 获取配置字符串值
* @param config 配置对象指针
* @param section 段名,没有段名时可以为NULL
* @param key 键名
* @param default_string 默认值
* @return 如果配置中有此键对应的值,返回该值,否则返回参数指定的默认值
*/
char* ini_config_get_string(INI_CONFIG* config,const char* section,const char* key,char* default_string);
/**
* 设置变量
* @param config 配置对象指针
* @param section 段名,没有段名时可以为NULL
* @param key 键名
* @param key_len 键长
* @param value 值
* @param value_len 值长度
* @return 成功为1,失败为0
*/
int ini_config_set_string(INI_CONFIG* config,const char* section,const char* key,int key_len,const char* value,int value_len);
/**
* 设置变量
* @param config 配置对象指针
* @param section 段名,没有段名时可以为NULL
* @param key 键名
* @param key_len 键长
* @param value 整数值
* @return 成功为1,失败为0
*/
int ini_config_set_int(INI_CONFIG* config,const char* section,const char* key,int key_len,int value);
/**
* 保存配置到文件中 *提示,原先配置文件中的注释信息将不会保存.
* @param config 配置对象指针
* @param filename 保存到的文件
* @return 成功为1,失败为0
*/
int ini_config_save(INI_CONFIG* config,const char* filename);
/**
* 类似于ini_config_save,只是参数是文件指针,此函数可以直接使用stdin,stdout,stderr. *提示:本函数不负责关闭fp.
* @param config 配置对象指针
* @param fp 文件指针
* @return 成功为1,失败为0
*/
int ini_config_print(INI_CONFIG* config,FILE* fp);
#endif
confile.c
/**
* INI配置文件管理函数库
* Ini file parse functions.
* By Hoverlees http://www.hoverlees.com me[at]hoverlees.com
*/
#include "confile.h"
#define MAX_SECTION_SIZE 64
typedef struct _PARSER{
int status;
int pos;
int start1;
int end1;
int start2;
int end2;
int row;
int col;
unsigned char* str;
int slen;
INI_CONFIG* config;
char section_name[MAX_SECTION_SIZE];
}PARSER;
typedef int (*PARSER_JUMP_FUNC)(PARSER* parser);
#define PARSE_STATUS_GET_KEY_OR_SECTION 1
#define PARSE_STATUS_GET_SECTION 2
#define PARSE_STATUS_GET_VALUE 3
#define PARSE_STATUS_COMMENT_LINE 4
CONFIG_BTREE_NODE* config_btree_find_node(CONFIG_BTREE* config,const char* key);
/**
* 内部使用函数,实现内存段trim,返回第一个非空字符指针及字符串trim后的长度.
*/
char* mem_trim(char* src,int len,int* outlen){
int start,end;
if(len==0) return NULL;
start=0;
end=len-1;
while(start<len && (src[start]==' ' || src[start]=='\r' || src[start]=='\n')){
start++;
}
while(end>start && (src[end]==' ' || src[end]=='\r' || src[end]=='\n')){
end--;
}
end=end+1;
if(start==end) return NULL;
src+=start;
*outlen=end-start;
return src;
}
/**
* 下面是存储配置专用的二叉树实现.
*/
CONFIG_BTREE_NODE* config_btree_create_node(const char* key,int key_len,void* data,int data_len){
char* p;
CONFIG_BTREE_NODE* node;
if(key_len==0) key_len=strlen(key);
if(data_len==0) data_len=strlen(data)+1;
node=(CONFIG_BTREE_NODE*) calloc(sizeof(CONFIG_BTREE_NODE)+key_len+data_len,1);
if(node!=NULL){
p=node->mem;
node->key=p;
p+=(key_len+1);
node->data=p;
memcpy(node->key,key,key_len);
memcpy(node->data,data,data_len);
node->left=NULL;
node->right=NULL;
}
return node;
}
int config_btree_free_node(CONFIG_BTREE_NODE* node){
free(node);
return 1;
}
CONFIG_BTREE* config_btree_create(){
CONFIG_BTREE* ret=(CONFIG_BTREE*) calloc(sizeof(CONFIG_BTREE),1);
return ret;
}
int config_btree_insert_node(CONFIG_BTREE* config,const char* key,int key_len,void* data,int data_len){
CONFIG_BTREE_NODE *p,**prev;
CONFIG_BTREE_NODE* node;
int comp;
if(key_len==0) key_len=strlen(key);
node=config_btree_create_node(key,key_len,data,data_len);
if(node==NULL) return 0;
p=config->root;
prev=&config->root;
if(!p){
config->root=node;
config->numNodes++;
return 1;
}
while(p){
comp=memcmp(key,p->key,key_len);
if(comp>0){
if(p->right==NULL){
p->right=node;
config->numNodes++;
return 1;
}
prev=&p->right;
p=p->right;
}
else if(comp<0){
if(p->left==NULL){
p->left=node;
config->numNodes++;
return 1;
}
prev=&p->left;
p=p->left;
}
else{
node->left=p->left;
node->right=p->right;
*prev=node;
config_btree_free_node(p);
return 2; //update
}
}
}
int config_btree_insert_section(CONFIG_BTREE* config,const char* section_name){
CONFIG_BTREE* section=config_btree_create();
if(section==NULL) return 0;
if(config_btree_insert_node(config,section_name,0,§ion,sizeof(void*))){
return 1;
}
else{
free(section);
return 0;
}
}
CONFIG_BTREE* config_btree_get_section(CONFIG_BTREE* config,const char* section_name){
CONFIG_BTREE* section;
CONFIG_BTREE_NODE* node;
node=config_btree_find_node(config,section_name);
if(node==NULL) return NULL;
memcpy(§ion,node->data,sizeof(void*));
return section;
}
int config_btree_insert_section_node(CONFIG_BTREE* config,const char* section_name,const char* key,
int key_len,void* data,int data_len){
CONFIG_BTREE* section;
CONFIG_BTREE_NODE* node;
node=config_btree_find_node(config,section_name);
if(node==NULL) return 0;
memcpy(§ion,node->data,sizeof(void*));
return config_btree_insert_node(section,key,key_len,data,data_len);
}
CONFIG_BTREE_NODE* config_btree_find_node(CONFIG_BTREE* config,const char* key){
CONFIG_BTREE_NODE* p;
int comp;
p=config->root;
while(p){
comp=strcmp(key,p->key);
if(comp>0){
p=p->right;
}
else if(comp<0){
p=p->left;
}
else{
return p;
}
}
return NULL;
}
int config_btree_delete_node(CONFIG_BTREE* config,const char* key){
CONFIG_BTREE_NODE* p;
CONFIG_BTREE_NODE* temp;
CONFIG_BTREE_NODE** prevTmpPos=NULL;
CONFIG_BTREE_NODE** prevPos=NULL;
int comp;
prevPos=&config->root;
p=config->root;
while(p){
comp=strcmp(key,p->key);
if(comp>0){
prevPos=&p->right;
p=p->right;
}
else if(comp<0){
prevPos=&p->left;
p=p->left;
}
else{
if(p->left){
temp=p->left;
while(temp->right){
prevTmpPos=&temp->right;
temp=temp->right;
}
if(prevTmpPos==NULL){
*prevPos=temp;
temp->right=p->right;
}
else{
if(temp->left){
*prevTmpPos=temp->left;
}
else{
*prevTmpPos=NULL;
}
*prevPos=temp;
temp->left=p->left;
temp->right=p->right;
}
config_btree_free_node(p);
}
else if(p->right){
temp=p->right;
while(temp->left){
prevTmpPos=&temp->left;
temp=temp->left;
}
if(prevTmpPos==NULL){
*prevPos=temp;
temp->left=p->left;
}
else{
if(temp->right){
*prevTmpPos=temp->right;
}
else{
*prevTmpPos=NULL;
}
*prevPos=temp;
temp->left=p->left;
temp->right=p->right;
}
config_btree_free_node(p);
}
else{
config_btree_free_node(p);
*prevPos=NULL;
}
config->numNodes--;
return 1;
}
}
return 0;
}
int config_btree_inorder_traverse(CONFIG_BTREE_NODE* node,CONFIG_BTREE_TRAVERSE_CB callback){
if(node==NULL) return 1;
if(!config_btree_inorder_traverse(node->left,callback)) return 0;
if(!callback(node)) return 0;
if(!config_btree_inorder_traverse(node->right,callback)) return 0;
return 1;
}
int config_btree_inorder_save_traverse(CONFIG_BTREE_NODE* node,FILE* fp,CONFIG_BTREE_SAVE_TRAVERSE_CB callback){
if(node==NULL) return 1;
if(!config_btree_inorder_save_traverse(node->left,fp,callback)) return 0;
if(!callback(fp,node)) return 0;
if(!config_btree_inorder_save_traverse(node->right,fp,callback)) return 0;
return 1;
}
int config_btree_preorder_traverse(CONFIG_BTREE_NODE* node,CONFIG_BTREE_TRAVERSE_CB callback){
if(node==NULL) return 1;
if(!callback(node)) return 0;
if(!config_btree_preorder_traverse(node->left,callback)) return 0;
if(!config_btree_preorder_traverse(node->right,callback)) return 0;
return 1;
}
int config_btree_postorder_traverse(CONFIG_BTREE_NODE* node,CONFIG_BTREE_TRAVERSE_CB callback){
if(node==NULL) return 1;
if(!config_btree_postorder_traverse(node->left,callback)) return 0;
if(!config_btree_postorder_traverse(node->right,callback)) return 0;
if(!callback(node)) return 0;
return 1;
}
int config_btree_destroy_section(CONFIG_BTREE_NODE* node){
CONFIG_BTREE* section;
if(!node) return 0;
memcpy(§ion,node->data,sizeof(void*));
config_btree_postorder_traverse(section->root,config_btree_free_node);
free(section);
}
int config_btree_destroy(CONFIG_BTREE* config){
if(!config) return 0;
config_btree_postorder_traverse(config->root,config_btree_destroy_section);
free(config);
return 1;
}
/**
* ini文件解析函数跳转表,此方式在大型解析的实现中非常高效.
*/
int parser_default_action(PARSER* parser){
parser->pos++;
parser->col++;
return 1;
}
int parse_default_gbk_action(PARSER* parser){
parser->pos+=2;
parser->col+=2;
return 1;
}
int parser_on_section_start(PARSER* parser){
if(parser->status==PARSE_STATUS_COMMENT_LINE){}
else if(parser->status==PARSE_STATUS_GET_KEY_OR_SECTION){
parser->start1=parser->pos+1;
parser->status=PARSE_STATUS_GET_SECTION;
}
parser->pos++;
parser->col++;
return 1;
}
int parser_on_section_end(PARSER* parser){
char* p;
int len,r;
if(parser->status==PARSE_STATUS_COMMENT_LINE){}
else if(parser->status==PARSE_STATUS_GET_SECTION){
memset(parser->section_name,0,MAX_SECTION_SIZE);
p=mem_trim(parser->str+parser->start1,parser->pos-parser->start1,&len);
if(p==NULL){//section段名不能为空
return 0;
}
memcpy(parser->section_name,p,len);
r=config_btree_insert_section(parser->config,parser->section_name);
if(r==0) return 0;//添加section失败
parser->status=PARSE_STATUS_GET_KEY_OR_SECTION;
parser->start1=parser->pos+1;
}
parser->pos++;
parser->col++;
return 1;
}
int parser_on_value_start(PARSER* parser){
char* p;
int len,r;
if(parser->status==PARSE_STATUS_GET_KEY_OR_SECTION){
parser->status=PARSE_STATUS_GET_VALUE;
parser->end1=parser->pos;
parser->start2=parser->pos+1;
}
parser->pos++;
parser->col++;
return 1;
}
int parser_on_new_line(PARSER* parser){
char *k,*v;
int klen,vlen;
switch(parser->status){
case PARSE_STATUS_COMMENT_LINE:
break;
case PARSE_STATUS_GET_VALUE:
k=mem_trim(parser->str+parser->start1,parser->end1-parser->start1,&klen);
v=mem_trim(parser->str+parser->start2,parser->pos-parser->start2,&vlen);
if(k==NULL) return 0;
if(v==NULL){
v="";
vlen=0;
}
if(!config_btree_insert_section_node(parser->config,parser->section_name,k,klen,v,vlen)) return 0;
break;
case PARSE_STATUS_GET_KEY_OR_SECTION:
break;
default:
return 0;
}
parser->start1=parser->pos+1;
parser->status=PARSE_STATUS_GET_KEY_OR_SECTION;
parser->pos++;
parser->col=1;
parser->row++;
return 1;
}
int parser_on_comment(PARSER* parser){
if(parser->col==1){
parser->status=PARSE_STATUS_COMMENT_LINE;
}
parser->col++;
parser->pos++;
return 1;
}
/**
* 接下来是ini配置管理的上层函数.
*/
INI_CONFIG* ini_config_create_from_string(unsigned char* str,int slen,int isGBK){
int r;
PARSER parser;
PARSER_JUMP_FUNC funcs[256];
INI_CONFIG* config=config_btree_create();
if(slen==0) slen=strlen(str);
strcpy(parser.section_name,"default");
parser.pos=0;
parser.status=PARSE_STATUS_GET_KEY_OR_SECTION;
parser.start1=0;
parser.str=str;
parser.slen=slen;
parser.row=1;
parser.col=1;
parser.config=config;
//初始化解析跳转表
if(isGBK){
for(r=0;r<127;r++){
funcs[r]=parser_default_action;
}
for(r=128;r<256;r++){
funcs[r]=parse_default_gbk_action;
}
}
else{
for(r=0;r<256;r++){
funcs[r]=parser_default_action;
}
}
funcs['[']=parser_on_section_start;
funcs[']']=parser_on_section_end;
funcs['=']=parser_on_value_start;
funcs['\n']=parser_on_new_line;
funcs[';']=parser_on_comment;
if(config!=NULL){
r=config_btree_insert_section(config,parser.section_name);
if(!r){
config_btree_destroy(config);
return NULL;
}
}
while(parser.pos<slen){
r=funcs[str[parser.pos]](&parser);
if(!r){ //解析错误,本代码不做任何提示,直接返回NULL,但可以从parser里在这里从parser里取得当前解析的文件位置和当前状态.
config_btree_destroy(config);
return NULL;
}
}
r=parser_on_new_line(&parser); if(!r){
config_btree_destroy(config);
return NULL;
}
return config;
}
INI_CONFIG* ini_config_create_from_file(const char* filename,int isGBK){
FILE* file;
INI_CONFIG* config;
char* buf;
struct stat s;
if(stat(filename,&s)) return NULL;
buf=malloc(s.st_size);
if(buf==NULL) return NULL;
file=fopen(filename,"r");
if(file==NULL){
free(buf);
return NULL;
}
fread(buf,s.st_size,1,file);
config=ini_config_create_from_string(buf,s.st_size,isGBK);
free(buf);
return config;
}
void ini_config_destroy(INI_CONFIG* config){
config_btree_destroy(config);
}
int ini_config_get_int(INI_CONFIG* config,const char* section,const char* key,int default_int){
CONFIG_BTREE_NODE* node;
INI_CONFIG* sec;
if(section==NULL) section="default";
sec=config_btree_get_section(config,section);
if(sec==NULL) return default_int;
node=config_btree_find_node(sec,key);
if(node==NULL) return default_int;
return atoi(node->data);
}
char* ini_config_get_string(INI_CONFIG* config,const char* section,const char* key,char* default_string){
CONFIG_BTREE_NODE* node;
INI_CONFIG* sec;
if(section==NULL) section="default";
sec=config_btree_get_section(config,section);
if(sec==NULL) return default_string;
node=config_btree_find_node(sec,key);
if(node==NULL) return default_string;
return (char*)node->data;
}
int ini_config_set_string(INI_CONFIG* config,const char* section,const char* key,int key_len,const char* value,int value_len){
CONFIG_BTREE* sect;
CONFIG_BTREE* node;
int r;
if(section==NULL) section="default";
sect=config_btree_get_section(config,section);
if(sect==NULL){
r=config_btree_insert_section(config,section);
if(!r) return 0;
sect=config_btree_get_section(config,section);
}
return config_btree_insert_node(sect,key,key_len,(void*)value,value_len);
}
int ini_config_set_int(INI_CONFIG* config,const char* section,const char* key,int key_len,int value){
char number[32];
int len=sprintf(number,"%d",value);
return ini_config_set_string(config,section,key,key_len,number,len);
}
int ini_config_save_traverse_value(FILE* fp,CONFIG_BTREE_NODE* node){
fprintf(fp,"%s=%s\n",node->key,(char*)node->data);
return 1;
}
int ini_config_save_traverse_section(FILE* fp,CONFIG_BTREE_NODE* node){
CONFIG_BTREE* section;
memcpy(§ion,node->data,sizeof(void*));
fprintf(fp,"[%s]\n",node->key);
config_btree_inorder_save_traverse(section->root,fp,ini_config_save_traverse_value);
return 1;
}
int ini_config_save(INI_CONFIG* config,const char* filename){
FILE* fp=fopen(filename,"w");
if(fp==NULL) return 0;
config_btree_inorder_save_traverse(config->root,fp,ini_config_save_traverse_section);
fclose(fp);
return 1;
}
int ini_config_print(INI_CONFIG* config,FILE* fp){
if(fp==NULL) return 0;
config_btree_inorder_save_traverse(config->root,fp,ini_config_save_traverse_section);
return 1;
}
二 json 做配置
2.0 介绍
这个不用多介绍了。铺天盖地的使用。
json不单用于配置文件的实现,也可以用于数据的传递
2.1 劣势
- 加注释不方便,即使我们用一个额外的字段来存储注释,也会被解析
- 解析和配置文件的结构强相关:如果Json的树形结构发生变化,解析实现也要跟着改,
即使实现一个可以解析无限深度json树的解析,但是业务层的更改也是很麻烦。 - 业务层使用有点复杂:如果是简单的配置很好用。如果json树的深度很深,业务层需要一个个节点的找下去。
业务中,我是用用milo的rapidjson,做简单的包装后使用
三 lua做配置文件
3.0 介绍
使用lua的table来做配置文件的数据存储,然后使用Lua原生的方式来遍历table.
3.1 优势
-
天生支持树形结构。
-
支持各种各样的key:可以是数字的key,也可以是string的key,甚至可以是对象key.
-
容易跨语言支持:lua可以直接使用,如果是其他语言,做一下lua接口的bind实现,就可以使用。
-
使用简单:比如我们想访问深层节点,业务层直接使用Get(‘root->node1->node12->node123’)这样子就可以获取到深层节点的内容
-
跟业务层弱关联:可以跟业务层无关联,解析是一个通用的解析,不同项目的业务,不需要根据业务重新实现一次解析,直接使用同一份解析,就可以直接使用。
-
功能强大:lua本身就是一门语言,可以提供很多功能,比如配置文件的错误检测,配置数据的数据注释等等。
-
跨平台:lua是基于 ANSC C实现的,几乎可以在所有平台上使用。本人就在嵌入式,单片机上使用过
-
十分动态:数据是可以直接写死在table,也可以是指向一个函数运行的结果,这样意味着,我们可以将配置数据跟一段逻辑相关联。比如,config={version=get_cur_version()}就表示了version这个字段可以根据get_cur_version来确定。甚至,我们可以将version指向网络请求返回的数据。你能想象,配置文件里面可以支持网络连接!!!!
3.2 劣势
- 需要学习一下lua。
- 怎么防止配置文件被注入破坏性代码,导致问题。因为lua的配置文件也是lua代码文件,里面可以放置可执行代码,如果被恶意放入代码,也被执行了,这个很大问题。
3.3 一个实现
3.3.1 lua的解析实现
lua解析的实现
--[[
]]
package.path = package.path ..';/home/config/?.lua;';
---------------------------------------------------------------------------------------------------------------------------------
--[[
if another config file use, change the require
]]
function get_config_file()
local config = require('config');
return config;
end
---------------------------------------------------------------------------------------------------------------------------------
--[[
]]
function GetRootInt(key)
local config = get_config_file();
local INVALID_VAL = -99999999;
for k,v in pairs(config) do
if k == key then
--print(v);
return v;
end
end
return INVALID_VAL;
end
--[[
]]
function GetRootString(key)
local config = get_config_file();
for k,v in pairs(config) do
if k == key then
return v;
end
end
return '';
end
--[[
get int array
]]
function GetRootIntArray(key)
DATA_CACHE.CleanCache();
local config = get_config_file();
for k,v in pairs(config) do
if k == key then
GetArrayIntData(v);
return;
end
end
end
--[[
get double array
]]
function GetRootDoubleArray(key)
DATA_CACHE.CleanCache();
local config = get_config_file();
for k,v in pairs(config) do
if k == key then
GetArrayDoubleData(v);
return;
end
end
end
--[[
get string array
]]
function GetRootStringArray(key)
DATA_CACHE.CleanCache();
local config = get_config_file();
for k,v in pairs(config) do
if k == key then
GetArrayStringData(v);
return;
end
end
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
--[[
]]
function GetChildInt(path,key)
local ret = get_child_val(path,key,-99999999);
if nil == ret then
return -99999999;
else
return ret;
end
end
function GetChildDouble(path,key)
local ret = get_child_val(path,key,-99999999);
if nil == ret then
return -99999999;
else
return ret;
end
end
--[[
]]
function GetChildString(path,key)
local ret = get_child_val(path,key,'');
if nil == ret then
return '';
else
return ret;
end
end
--[[
]]
function GetChildIntArray(path,key)
DATA_CACHE.CleanCache();
local split_key = '-';
local path_list = str_split(path,split_key);
local path_size = #path_list;
--[[
child node should at lease have 2 path, one for root,one for child node
]]
if 1 >= path_size then
return;
end
local config = get_config_file();
--first node is root,remove it
table.remove(path_list,1);
local right_node = get_right_node(path_list,config);
if nil == right_node then
return;
end
--TODO: should check right_node is table
for k,v in pairs(right_node) do
if k == key then
GetArrayIntData(v);
return;
end
end
end
--[[
]]
function GetChildDoubleArray(path,key)
DATA_CACHE.CleanCache();
local split_key = '-';
local path_list = str_split(path,split_key);
local path_size = #path_list;
--[[
child node should at lease have 2 path, one for root,one for child node
]]
if 1 >= path_size then
return;
end
local config = get_config_file();
--first node is root,remove it
table.remove(path_list,1);
local right_node = get_right_node(path_list,config);
if nil == right_node then
return;
end
--TODO: should check right_node is table
for k,v in pairs(right_node) do
if k == key then
GetArrayDoubleData(v);
return;
end
end
end
--[[
]]
function GetChildStringArray(path,key)
DATA_CACHE.CleanCache();
local split_key = '-';
local path_list = str_split(path,split_key);
local path_size = #path_list;
--[[
child node should at lease have 2 path, one for root,one for child node
]]
if 1 >= path_size then
return;
end
local config = get_config_file();
--first node is root,remove it
table.remove(path_list,1);
local right_node = get_right_node(path_list,config);
if nil == right_node then
return;
end
--TODO: should check right_node is table
for k,v in pairs(right_node) do
if k == key then
GetArrayStringData(v);
return;
end
end
end
--[[
internal use
]]
function get_child_val(path,key,INVALID_VAL)
local split_key = '-';
local path_list = str_split(path,split_key);
local path_size = #path_list;
--[[
child node should at lease have 2 path, one for root,one for child node
]]
if 1 >= path_size then
return INVALID_VAL-1;
end
local config = get_config_file();
--first node is root,remove it
table.remove(path_list,1);
local right_node = get_right_node(path_list,config);
if nil == right_node then
return INVALID_VAL -2;
end
return right_node[key];
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
--[[
find the deepest child node
]]
function get_right_node(path_list,config)
local cur_child_node_name = path_list[1];
for k,v in pairs(config) do
if k == cur_child_node_name then
--find the node
if 1 == #path_list then
--final_node
return v;
else
--go deeper node
table.remove(path_list,1);
return get_right_node(path_list,v);
end
end
end
return nil;
end
--[[
]]
function str_split(str,reps)
local resultStrList = {}
string.gsub(str,'[^'..reps..']+',function (w)
table.insert(resultStrList,w)
end)
return resultStrList
end
--[[
by default,if we don't set index by hand,the index start with 1,and inc one by one,so we can get the value in declare sort,so can use ipairs
]]
function GetArrayIntData(list)
for k,v in ipairs(list) do
print(v);
DATA_CACHE.AddArrayIntElement(v);
end
end
--[[
by default,if we don't set index by hand,the index start with 1,and inc one by one,so we can get the value in declare sort,so can use [ipairs]
]]
function GetArrayDoubleData(list)
for k,v in ipairs(list) do
print(v);
DATA_CACHE.AddArrayDoubleElement(v);
end
end
--[[
by default,if we don't set index by hand,the index start with 1,and inc one by one,so we can get the value in declare sort,so can use [ipairs]
]]
function GetArrayStringData(list)
for k,v in ipairs(list) do
print(v);
DATA_CACHE.AddArrayStringElement(v);
end
end
3.3.2 因为lua返回数值给其他语言不是很方便,所以我做了一个简单的c++bind,用于支持数组
c++提供给lua接口使用的缓存接口LuaCacheInterface.h
#ifndef LUACACHEINTERFACE_H
#define LUACACHEINTERFACE_H
//extern "C"
//{
//#include "lua.h"
//#include "lauxlib.h"
//#include "lualib.h"
//}
#include <string>
typedef struct lua_State lua_State;
typedef int (*lua_CFunction)(lua_State *L);
struct ieo_lua_reg_t
{
const char *name ;
lua_CFunction func;
};
#define IEO_LUA_FUNC_INIT(p) { #p, (lua_CFunction)p }
class LuaCacheInterface
{
public:
LuaCacheInterface();
public:
virtual int RegistLib(const char *libname, ieo_lua_reg_t *reglist) = 0;
virtual void CleanAll() = 0;
virtual int AddIntArrayElement(int val) = 0;
virtual int AddDoubleArrayElement(double val) = 0;
virtual int AddStringArrayElement(std::string &val) = 0;
};
#endif // LUACACHEINTERFACE_H
提供给lua使用的API定义:LuaCacheFunction.h
#ifndef LUACACHEFUNCTION_H
#define LUACACHEFUNCTION_H
class LuaCacheInterface;
bool RegisterCacheFunc(LuaCacheInterface *obj);
#endif // LUACACHEFUNCTION_H
LuaCacheFunction.cpp
#include "LuaCacheFunction.h"
#include "LuaCacheInterface.h"
#include "GlobalKey.h"
#include <iostream>
#include <vector>
using namespace std;
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
namespace IEO_LUA_CACHE_INTERFACE {
typedef std::vector<ieo_lua_reg_t> IEO_FUNC_INFO;
static int CleanCache(lua_State *L)
{
//get the cache ptr
// void *key_addr = (void *) &lua_cache_key;
// lua_pushlightuserdata(L, GLOBAL_KEY::GetKeyAddr());
// lua_gettable(L, LUA_REGISTRYINDEX);
// void *ptr = lua_touserdata(L, -1);
lua_getfield(L, LUA_REGISTRYINDEX, GLOBAL_KEY::CACHE_LUA_KEY);
void *ptr = lua_touserdata(L, -1);
LuaCacheInterface *obj = (LuaCacheInterface *) ptr;
obj->CleanAll();
return 0;
}
/**
* @brief AddArrayIntElement
* @param L
* @return
*/
static int AddArrayIntElement(lua_State *L)
{
int val = lua_tonumber(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, GLOBAL_KEY::CACHE_LUA_KEY);
void *ptr = lua_touserdata(L, -1);
LuaCacheInterface *obj = (LuaCacheInterface *) ptr;
obj->AddIntArrayElement(val);
cout << "add int:" << val << endl;
cout << flush;
return 0;
}
static int AddArrayDoubleElement(lua_State *L)
{
double val = lua_tonumber(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, GLOBAL_KEY::CACHE_LUA_KEY);
void *ptr = lua_touserdata(L, -1);
LuaCacheInterface *obj = (LuaCacheInterface *) ptr;
obj->AddDoubleArrayElement(val);
cout << "add double:" << val << endl;
cout << flush;
return 0;
}
static int AddArrayStringElement(lua_State *L)
{
std::string val = std::string(lua_tostring(L, 1));
lua_getfield(L, LUA_REGISTRYINDEX, GLOBAL_KEY::CACHE_LUA_KEY);
void *ptr = lua_touserdata(L, -1);
LuaCacheInterface *obj = (LuaCacheInterface *) ptr;
obj->AddStringArrayElement(val);
return 0;
}
/*
* 由vector转换成数组
*/
static ieo_lua_reg_t *to_func_list(IEO_FUNC_INFO &tmp_list)
{
ieo_lua_reg_t *tmp_ptr;
int size = tmp_list.size();
tmp_ptr = new ieo_lua_reg_t[size + 1];
for (int i = 0; i < size; ++i)
{
// Msg("rigister function[%s]\n", tmp_list.at(i).name);
tmp_ptr[i] = tmp_list.at(i);
}
tmp_ptr[size] = {NULL, NULL}; //要已NULL结尾,不然不同平台可能会有问题
return tmp_ptr;
}
static ieo_lua_reg_t func_list[] =
{
IEO_LUA_FUNC_INIT(CleanCache),
IEO_LUA_FUNC_INIT(AddArrayIntElement),
IEO_LUA_FUNC_INIT(AddArrayDoubleElement),
IEO_LUA_FUNC_INIT(AddArrayStringElement),
{0, 0}
};
}
bool RegisterCacheFunc(LuaCacheInterface *obj)
{
IEO_LUA_CACHE_INTERFACE::IEO_FUNC_INFO tmp_list;
ieo_lua_reg_t *tmp = IEO_LUA_CACHE_INTERFACE::func_list;
for (; tmp->name; ++tmp)
{
tmp_list.push_back(*tmp);
}
ieo_lua_reg_t *func_list = IEO_LUA_CACHE_INTERFACE::to_func_list(tmp_list);
obj->RegistLib("DATA_CACHE", func_list);
return false;
}
c++中获取配置数据的实现 ConfigFileUtil.h
/**
*
* @brief use lua as load config file tools
*
*added by xsq,2022-09-01
*/
#ifndef CONFIGFILEUTIL_H
#define CONFIGFILEUTIL_H
#include <string>
#include <vector>
#include "LuaCacheInterface.h"
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
class ConfigFileUtil : public LuaCacheInterface
{
public:
const int INVALID_VAL = -99999999;
public:
ConfigFileUtil();
virtual ~ ConfigFileUtil();
public:
//-------------------------------------------------
//root node
int GetInt(std::string &key);
std::string GetString(std::string &key);
int GetIntArray(std::vector<int> &buf, std::string &key);
int GetDoubleArray(std::vector<double> &buf, std::string &key);
int GetStringArray(std::vector<std::string> &buf, std::string &key);
//child node
int GetChildInt(std::string &path, std::string &key);
double GetChildDouble(std::string &path, std::string &key);
std::string GetChildString(std::string &path, std::string &key);
int GetChildIntArray(std::vector<int> &buf, std::string &path, std::string &key);
int GetChildDoubleArray(std::vector<double> &buf, std::string &path, std::string &key);
int GetChildStringArray(std::vector<std::string> &buf, std::string &path, std::string &key);
//-------------------------------------------------
public:
int Init(std::string &file_name);
bool IsError() {return m_bErrorOccur;}
std::string GetDesc() {return m_desc;}
void Close();
public:
//------------------------------------------------------------------------------------
//LuaCacheInterface interface
//for lua use ,do not call by hand
virtual int RegistLib(const char *libname, ieo_lua_reg_t *reglist);
virtual void CleanAll();
virtual int AddIntArrayElement(int val);
virtual int AddDoubleArrayElement(double val);
virtual int AddStringArrayElement(std::string &val);
//------------------------------------------------------------------------------------
private:
void load_lua_libs();
void fatal_error_occur();
void warning_error_occur();
private:
bool m_bLoaded;
bool m_bErrorOccur;
std::string m_desc;
lua_State *m_state;
private:
std::vector<int> m_int_array;
std::vector<double> m_double_array;
std::vector<std::string> m_str_array;
};
#endif // CONFIGFILEUTIL_H
ConfigFileUtil.cpp
#include "ConfigFileUtil.h"
#include "LuaCacheFunction.h"
#include "GlobalKey.h"
#include <iostream>
using namespace std;
ConfigFileUtil::ConfigFileUtil()
{
m_state = NULL;
m_bLoaded = false;
}
ConfigFileUtil::~ConfigFileUtil()
{
Close();
}
int ConfigFileUtil::RegistLib(const char *libname, ieo_lua_reg_t *reglist)
{
luaL_openlib(m_state, libname, (luaL_reg *) reglist, 0);
//----------------------------------------------------------------
// lua_State *L = m_state;
// lua_pushlightuserdata(L, (void *) &GLOBAL_KEY::CACHE_LUA_KEY1) ; /**取静态变量i的地址作为key压栈**/
// lua_pushlightuserdata(L, this); /**把值压栈**/
// lua_settable(L, LUA_REGISTRYINDEX); /** &i, value出栈;并且实现register[&i] = value**/
//----------------------------------------------------------------
lua_pushlightuserdata(m_state, this);
lua_setfield(m_state, LUA_REGISTRYINDEX, GLOBAL_KEY::CACHE_LUA_KEY);
//----------------------------------------------------------------
// lua_State *L = m_state;
// static int i = 0;
// lua_pushlightuserdata(L, (void *) &GLOBAL_KEY::CACHE_LUA_KEY1) ; /**取静态变量i的地址作为key压栈**/
// lua_pushlightuserdata(L, this); /**把值压栈**/
// lua_settable(L, LUA_REGISTRYINDEX); /** &i, value出栈;并且实现register[&i] = value**/
// lua_pushlightuserdata(L, (void *) & GLOBAL_KEY::CACHE_LUA_KEY1) ; /**取静态变量i的地址作为key压栈**/
// lua_gettable(L, LUA_REGISTRYINDEX); /**获取value值,如果函数调用成功,那么value目前在栈顶**/
// void *ptr1 = lua_touserdata(L, -1);
// LuaCacheInterface *ptr = (LuaCacheInterface *)ptr1;
// return 0;
return 0;
}
void ConfigFileUtil::CleanAll()
{
m_int_array.clear();
m_double_array.clear();
m_str_array.clear();
}
int ConfigFileUtil::AddIntArrayElement(int val)
{
m_int_array.push_back(val);
return 0;
}
int ConfigFileUtil::AddDoubleArrayElement(double val)
{
m_double_array.push_back(val);
return 0;
}
int ConfigFileUtil::AddStringArrayElement(std::string &val)
{
m_str_array.push_back(val);
return 0;
}
int ConfigFileUtil::Init(std::string &file_name)
{
if (true == m_bLoaded)
{
//because of Register lib,can't Init twice
return 0;
}
load_lua_libs();
int ret = luaL_loadfile(m_state, file_name.data());
if (0 != ret)
{
//load file fail
return ret;
}
ret = lua_pcall(m_state, 0, 0, 0);
if (0 != ret)
{
//load file fail
return ret;
}
m_bLoaded = true;
m_bErrorOccur = false;
RegisterCacheFunc(this);
//pre alloc
int pre_alloc_size = 100;
m_int_array.reserve(pre_alloc_size);
m_double_array.reserve(pre_alloc_size);
m_str_array.reserve(pre_alloc_size);
return 0;
}
void ConfigFileUtil::Close()
{
if (NULL != m_state)
{
lua_close(m_state);
m_state = NULL;
}
}
int ConfigFileUtil::GetInt(std::string &key)
{
if (false == m_bLoaded)
{
return INVALID_VAL;
}
lua_getglobal(m_state, "GetRootInt");
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 1, 1, 0);
if (0 == ret)
{
//no error
int val = static_cast<int>(lua_tonumber(m_state, -1));
return val;
}
else
{
fatal_error_occur();
return INVALID_VAL;
}
}
std::string ConfigFileUtil::GetString(std::string &key)
{
if (false == m_bLoaded)
{
return std::string("");
}
lua_getglobal(m_state, "GetRootInt");
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 1, 1, 0);
if (0 == ret)
{
//no error
std::string val = std::string(lua_tostring(m_state, -1));
return val;
}
else
{
fatal_error_occur();
return std::string("");
}
}
int ConfigFileUtil::GetIntArray(std::vector<int> &buf, std::string &key)
{
if (false == m_bLoaded)
{
return -1;
}
lua_getglobal(m_state, "GetRootIntArray");
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 1, 0, 0);
if (0 == ret)
{
//no error
cout << "GetIntArray not error" << endl << flush;
buf.clear();
for (int i = 0; i < m_int_array.size(); ++i)
{
buf.push_back(m_int_array[i]);
}
}
else
{
cout << "GetIntArray error" << endl << flush;
fatal_error_occur();
return -2;
}
return 0;
//----------------------------------------
// lua_State *L = m_state;
// lua_pushlightuserdata(L, (void *) &GLOBAL_KEY::CACHE_LUA_KEY1) ; /**取静态变量i的地址作为key压栈**/
// lua_pushlightuserdata(L, this); /**把值压栈**/
// lua_settable(L, LUA_REGISTRYINDEX); /** &i, value出栈;并且实现register[&i] = value**/
// lua_pushlightuserdata(L, (void *) & GLOBAL_KEY::CACHE_LUA_KEY1) ; /**取静态变量i的地址作为key压栈**/
// lua_gettable(L, LUA_REGISTRYINDEX); /**获取value值,如果函数调用成功,那么value目前在栈顶**/
// void *ptr1 = lua_touserdata(L, -1);
// LuaCacheInterface *ptr = (LuaCacheInterface *)ptr1;
// return 0;
}
int ConfigFileUtil::GetDoubleArray(std::vector<double> &buf, std::string &key)
{
if (false == m_bLoaded)
{
return -1;
}
lua_getglobal(m_state, "GetRootDoubleArray");
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 1, 0, 0);
if (0 == ret)
{
//no error
cout << "GetDoubleArray not error" << endl << flush;
buf.clear();
for (int i = 0; i < m_double_array.size(); ++i)
{
buf.push_back(m_double_array[i]);
}
}
else
{
cout << "GetDoubleArray error" << endl << flush;
fatal_error_occur();
return -2;
}
return 0;
}
int ConfigFileUtil::GetStringArray(std::vector<std::string> &buf, std::string &key)
{
if (false == m_bLoaded)
{
return -1;
}
lua_getglobal(m_state, "GetRootStringArray");
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 1, 0, 0);
if (0 == ret)
{
//no error
cout << "GetRootStringArray not error" << endl << flush;
buf.clear();
for (int i = 0; i < m_str_array.size(); ++i)
{
buf.push_back(m_str_array[i]);
}
}
else
{
cout << "GetRootStringArray error" << endl << flush;
fatal_error_occur();
return -2;
}
return 0;
}
/**
* @brief ConfigFileUtil::GetChildInt
* @param path data node path,for example:config-child means child node of config
* @param key
* @return
*/
int ConfigFileUtil::GetChildInt(std::string &path, std::string &key)
{
if (false == m_bLoaded)
{
return INVALID_VAL;
}
lua_getglobal(m_state, "GetChildInt");
lua_pushstring(m_state, path.data());
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 2, 1, 0);
if (0 == ret)
{
//no error
cout << "GetChildInt not error" << endl << flush;
int val = static_cast<int>(lua_tonumber(m_state, -1));
return val;
}
else
{
cout << "GetChildInt error" << endl << flush;
fatal_error_occur();
return INVALID_VAL;
}
}
double ConfigFileUtil::GetChildDouble(std::string &path, std::string &key)
{
if (false == m_bLoaded)
{
return INVALID_VAL;
}
lua_getglobal(m_state, "GetChildDouble");
lua_pushstring(m_state, path.data());
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 2, 1, 0);
if (0 == ret)
{
//no error
cout << "GetChildDouble not error" << endl << flush;
double val = lua_tonumber(m_state, -1);
cout << val << endl << flush;
return val;
}
else
{
cout << "GetChildDouble error" << endl << flush;
fatal_error_occur();
return INVALID_VAL;
}
}
std::string ConfigFileUtil::GetChildString(std::string &path, std::string &key)
{
if (false == m_bLoaded)
{
return std::string("");
}
lua_getglobal(m_state, "GetChildString");
lua_pushstring(m_state, path.data());
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 2, 1, 0);
if (0 == ret)
{
//no error
cout << "GetChildString not error" << endl << flush;
std::string val = std::string(lua_tostring(m_state, -1));
return val;
}
else
{
cout << "GetChildString error" << endl << flush;
fatal_error_occur();
return std::string("");
}
}
int ConfigFileUtil::GetChildIntArray(std::vector<int> &buf, std::string &path, std::string &key)
{
if (false == m_bLoaded)
{
return -1;
}
lua_getglobal(m_state, "GetChildIntArray");
lua_pushstring(m_state, path.data());
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 2, 0, 0);
if (0 == ret)
{
//no error
cout << "GetChildIntArray not error" << endl << flush;
buf.clear();
for (int i = 0; i < m_int_array.size(); ++i)
{
buf.push_back(m_int_array[i]);
}
}
else
{
cout << "GetChildIntArray error" << endl << flush;
fatal_error_occur();
return -2;
}
return 0;
}
int ConfigFileUtil::GetChildDoubleArray(std::vector<double> &buf, std::string &path, std::string &key)
{
if (false == m_bLoaded)
{
return -1;
}
lua_getglobal(m_state, "GetChildDoubleArray");
lua_pushstring(m_state, path.data());
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 2, 0, 0);
if (0 == ret)
{
//no error
cout << "GetChildDoubleArray not error" << endl << flush;
buf.clear();
for (int i = 0; i < m_double_array.size(); ++i)
{
buf.push_back(m_double_array[i]);
}
}
else
{
cout << "GetChildDoubleArray error" << endl << flush;
fatal_error_occur();
return -2;
}
return 0;
}
int ConfigFileUtil::GetChildStringArray(std::vector<std::string> &buf, std::string &path, std::string &key)
{
if (false == m_bLoaded)
{
return -1;
}
lua_getglobal(m_state, "GetChildStringArray");
lua_pushstring(m_state, path.data());
lua_pushstring(m_state, key.data());
int ret = lua_pcall(m_state, 2, 0, 0);
if (0 == ret)
{
//no error
cout << "GetChildStringArray not error" << endl << flush;
buf.clear();
for (int i = 0; i < m_str_array.size(); ++i)
{
buf.push_back(m_str_array[i]);
}
}
else
{
cout << "GetChildStringArray error" << endl << flush;
fatal_error_occur();
return -2;
}
return 0;
}
void ConfigFileUtil::load_lua_libs()
{
if (NULL == m_state)
{
m_state = luaL_newstate();
luaL_openlibs(m_state);
}
}
void ConfigFileUtil::fatal_error_occur()
{
m_desc = std::string(lua_tostring(m_state, -1));
m_bLoaded = false;
}
void ConfigFileUtil::warning_error_occur()
{
}
lua的一个简单配置:
local Config = {};
Config={
Kp={2.0,2.0},
Kd={0.01,0.01},
child1_node={
child2_node={
data={123,999.0,1444} --data is the what we want to read
}
}
};
return Config;
c++使用演示:
ConfigFileUtil config;
std::string config_file = "/home/test/config/LoadConfig.lua";
config.Init(config_file);
std::string path = std::string("Config-child1_node->child2_node");
std::string key = std::string("data");
std::vector<double> buf1;
config.GetChildDoubleArray(buf1, path, key);