• 配置文件中的ini,json,以及lua实现方式的优劣比较


    by Laniakea.White


    一 ini做配置

    1.0 ini例子

    [test1]
    key11=val1
    key12 = val2
    
    [test2]
    key21=val23
    
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.1 实现方式

    只使用语言中最基本的字符串分割方式,或者使用语言自带的正则表达式,对配置内容进行分割,然后解析出来.
    windows的话,好像有很古老的API,可以支持解析ini配置文件的。

    ##1.2 优势
    1.通用好: 实现所依赖的功能,是语言中最基本的东西,这就意味着,你可以在十分底层的代码中使用,而不需要引入新的库。比如,在嵌入式或者单片机中都是可以使用。

    我自己就是拿来做程序引导时的配置解析的。

    1.3 劣势

    1. 不容易做复杂的配置:ini这种结构,要做复杂的配置不太容易,通常使用都是很简单的key-value的方式。比如,如果要做二级子节点,就不行了。
    2. 解析麻烦:还是最原始的分割,然后解析
    3. 容易出错: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 
    #include 
    #include 
    #include 
    #include 
    //#include 
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    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,&section,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(&section,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(&section,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(&section,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(&section,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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515

    二 json 做配置

    2.0 介绍

    这个不用多介绍了。铺天盖地的使用。
    json不单用于配置文件的实现,也可以用于数据的传递

    2.1 劣势

    1. 加注释不方便,即使我们用一个额外的字段来存储注释,也会被解析
    2. 解析和配置文件的结构强相关:如果Json的树形结构发生变化,解析实现也要跟着改,
      即使实现一个可以解析无限深度json树的解析,但是业务层的更改也是很麻烦。
    3. 业务层使用有点复杂:如果是简单的配置很好用。如果json树的深度很深,业务层需要一个个节点的找下去。

    业务中,我是用用milo的rapidjson,做简单的包装后使用


    lua做配置文件

    3.0 介绍

    使用lua的table来做配置文件的数据存储,然后使用Lua原生的方式来遍历table.

    3.1 优势

    1. 天生支持树形结构

    2. 支持各种各样的key:可以是数字的key,也可以是string的key,甚至可以是对象key.

    3. 容易跨语言支持:lua可以直接使用,如果是其他语言,做一下lua接口的bind实现,就可以使用。

    4. 使用简单:比如我们想访问深层节点,业务层直接使用Get(‘root->node1->node12->node123’)这样子就可以获取到深层节点的内容

    5. 跟业务层弱关联:可以跟业务层无关联,解析是一个通用的解析,不同项目的业务,不需要根据业务重新实现一次解析,直接使用同一份解析,就可以直接使用。

    6. 功能强大:lua本身就是一门语言,可以提供很多功能,比如配置文件的错误检测,配置数据的数据注释等等。

    7. 跨平台:lua是基于 ANSC C实现的,几乎可以在所有平台上使用。本人就在嵌入式,单片机上使用过

    8. 十分动态:数据是可以直接写死在table,也可以是指向一个函数运行的结果,这样意味着,我们可以将配置数据跟一段逻辑相关联。比如,config={version=get_cur_version()}就表示了version这个字段可以根据get_cur_version来确定。甚至,我们可以将version指向网络请求返回的数据。你能想象,配置文件里面可以支持网络连接!!!!

    3.2 劣势

    1. 需要学习一下lua。
    2. 怎么防止配置文件被注入破坏性代码,导致问题。因为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
    
    
     
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332

    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 
    
    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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    提供给lua使用的API定义:LuaCacheFunction.h

    #ifndef LUACACHEFUNCTION_H
    #define LUACACHEFUNCTION_H
    
    
    class LuaCacheInterface;
    
    bool RegisterCacheFunc(LuaCacheInterface *obj);
    
    
    #endif // LUACACHEFUNCTION_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    LuaCacheFunction.cpp

     #include "LuaCacheFunction.h"
    #include "LuaCacheInterface.h"
    #include "GlobalKey.h"
    #include 
    #include 
    using namespace std;
    
    extern "C"
    {
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
    }
    
    
    namespace IEO_LUA_CACHE_INTERFACE {
    
    typedef std::vector 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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126

      c++中获取配置数据的实现 ConfigFileUtil.h

      /**
       *
       * @brief  use lua as load config file tools
       *
       *added by xsq,2022-09-01
       */
      #ifndef CONFIGFILEUTIL_H
      #define CONFIGFILEUTIL_H
      
      #include 
      #include 
      #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 &buf, std::string &key);
          int GetDoubleArray(std::vector &buf, std::string &key);
          int GetStringArray(std::vector &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 &buf, std::string &path, std::string &key);
          int GetChildDoubleArray(std::vector &buf, std::string &path, std::string &key);
          int GetChildStringArray(std::vector &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 m_int_array;
          std::vector m_double_array;
          std::vector m_str_array;
      
      
      };
      
      #endif // CONFIGFILEUTIL_H
      
      
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88

      ConfigFileUtil.cpp

      #include "ConfigFileUtil.h"
      #include "LuaCacheFunction.h"
      #include "GlobalKey.h"
      #include 
      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(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 &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 &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 &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(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 &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 &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 &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()
      {
      
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100
      • 101
      • 102
      • 103
      • 104
      • 105
      • 106
      • 107
      • 108
      • 109
      • 110
      • 111
      • 112
      • 113
      • 114
      • 115
      • 116
      • 117
      • 118
      • 119
      • 120
      • 121
      • 122
      • 123
      • 124
      • 125
      • 126
      • 127
      • 128
      • 129
      • 130
      • 131
      • 132
      • 133
      • 134
      • 135
      • 136
      • 137
      • 138
      • 139
      • 140
      • 141
      • 142
      • 143
      • 144
      • 145
      • 146
      • 147
      • 148
      • 149
      • 150
      • 151
      • 152
      • 153
      • 154
      • 155
      • 156
      • 157
      • 158
      • 159
      • 160
      • 161
      • 162
      • 163
      • 164
      • 165
      • 166
      • 167
      • 168
      • 169
      • 170
      • 171
      • 172
      • 173
      • 174
      • 175
      • 176
      • 177
      • 178
      • 179
      • 180
      • 181
      • 182
      • 183
      • 184
      • 185
      • 186
      • 187
      • 188
      • 189
      • 190
      • 191
      • 192
      • 193
      • 194
      • 195
      • 196
      • 197
      • 198
      • 199
      • 200
      • 201
      • 202
      • 203
      • 204
      • 205
      • 206
      • 207
      • 208
      • 209
      • 210
      • 211
      • 212
      • 213
      • 214
      • 215
      • 216
      • 217
      • 218
      • 219
      • 220
      • 221
      • 222
      • 223
      • 224
      • 225
      • 226
      • 227
      • 228
      • 229
      • 230
      • 231
      • 232
      • 233
      • 234
      • 235
      • 236
      • 237
      • 238
      • 239
      • 240
      • 241
      • 242
      • 243
      • 244
      • 245
      • 246
      • 247
      • 248
      • 249
      • 250
      • 251
      • 252
      • 253
      • 254
      • 255
      • 256
      • 257
      • 258
      • 259
      • 260
      • 261
      • 262
      • 263
      • 264
      • 265
      • 266
      • 267
      • 268
      • 269
      • 270
      • 271
      • 272
      • 273
      • 274
      • 275
      • 276
      • 277
      • 278
      • 279
      • 280
      • 281
      • 282
      • 283
      • 284
      • 285
      • 286
      • 287
      • 288
      • 289
      • 290
      • 291
      • 292
      • 293
      • 294
      • 295
      • 296
      • 297
      • 298
      • 299
      • 300
      • 301
      • 302
      • 303
      • 304
      • 305
      • 306
      • 307
      • 308
      • 309
      • 310
      • 311
      • 312
      • 313
      • 314
      • 315
      • 316
      • 317
      • 318
      • 319
      • 320
      • 321
      • 322
      • 323
      • 324
      • 325
      • 326
      • 327
      • 328
      • 329
      • 330
      • 331
      • 332
      • 333
      • 334
      • 335
      • 336
      • 337
      • 338
      • 339
      • 340
      • 341
      • 342
      • 343
      • 344
      • 345
      • 346
      • 347
      • 348
      • 349
      • 350
      • 351
      • 352
      • 353
      • 354
      • 355
      • 356
      • 357
      • 358
      • 359
      • 360
      • 361
      • 362
      • 363
      • 364
      • 365
      • 366
      • 367
      • 368
      • 369
      • 370
      • 371
      • 372
      • 373
      • 374
      • 375
      • 376
      • 377
      • 378
      • 379
      • 380
      • 381
      • 382
      • 383
      • 384
      • 385
      • 386
      • 387
      • 388
      • 389
      • 390
      • 391
      • 392
      • 393
      • 394
      • 395
      • 396
      • 397
      • 398
      • 399
      • 400
      • 401
      • 402
      • 403
      • 404
      • 405
      • 406
      • 407
      • 408
      • 409
      • 410
      • 411
      • 412
      • 413
      • 414
      • 415
      • 416
      • 417
      • 418
      • 419
      • 420
      • 421
      • 422
      • 423
      • 424
      • 425
      • 426
      • 427
      • 428
      • 429
      • 430
      • 431
      • 432
      • 433
      • 434
      • 435
      • 436
      • 437
      • 438
      • 439
      • 440
      • 441
      • 442
      • 443
      • 444
      • 445
      • 446
      • 447
      • 448
      • 449
      • 450
      • 451
      • 452
      • 453
      • 454
      • 455
      • 456
      • 457
      • 458
      • 459
      • 460
      • 461
      • 462
      • 463
      • 464
      • 465
      • 466
      • 467
      • 468
      • 469
      • 470
      • 471
      • 472
      • 473
      • 474
      • 475
      • 476
      • 477
      • 478
      • 479
      • 480
      • 481
      • 482
      • 483
      • 484

      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;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13

      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 buf1;
          config.GetChildDoubleArray(buf1, path, key);
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • 相关阅读:
      学习之浅谈python如何做接口自动化
      QT第2课-GUI程序实例分析
      解决:PowerDesigne找不到右边表的工具栏
      【Python从入门到进阶】67、Pandas使用stack和pivot实现数据透视
      【若依】前后端分离版本部署到Tomcat完整过程
      《QT从基础到进阶·二十九》QT,opencv源码调试
      记录:Unity脚本的编写5.0
      TortoiseSVN使用情形、安装与使用
      时间工具类-- LocalDateTimeUtil详解
      深耕5G,芯讯通持续推动5G应用百花齐放
    • 原文地址:https://blog.csdn.net/feisy/article/details/126687900