UtilBox(ub)基础组件 -- ConfigureLoader文件配置读取模块

来源:互联网 发布:求主析取范式算法 编辑:程序博客网 时间:2024/04/30 17:36

      好久没更新博客了哈,今天抽空把之前写了一部分的东西拿出来继续分享。

      linux的getopt()和getopt_long()大家都用过,读取命令行参数,比如./test -h 127.0.0.1 -c 100 --port 8080类似这样的。好多脚本语言python,shell这样获取比较简单直接sh就可以了(比如echo "./server --start --port 8080" >> ./start.sh)。有些软件为了方便,都有一个configure文件,通过对configure文件的修改来实现软件的配置控制,熟悉Java开发的对此应该很有感触,Tomcat/Apache的webserver,配置文件一大堆,基本不太可能通过启动时候传入,很多Linux软件都是通过configure文件来设置。

      通过configure文件的方式,既方便了传入配置的启动,还可以在文件中针对某一个配置进行注释性的说明,而且也方便查阅。

      OK,下面就来说一下这个ConfigureLoader(一下简称cfgloader),它主要实现了对文件中文本的行解析,配置都是通过KeyValue对来编写,可以添加自己的注释(以#开头) :

#server端口号port : 9999#这是关于线程数的#设置threads : 10#最大连接数max_conns : 1024

      KeyValue通过":"冒号来分割,并忽略其中的空格。下面来说一下接口,首先一个conf_load(const char*)来获取用户的数据,为什么不用FILE*或者filedescriptor,因为这样限制比较大,这些配置也接受来自getopt哈。然后就是相应的获取KV的接口,conf_get(const char* key),通过传入key获取value,这里可以得到的数据类型是整数或者字符串。如果查询不到或者转换失败会返回NULL或者0。这里有一个小问题待考究,就是conf_get_integer返回long,而long是atol()函数获得的,所以即使用户写的不是数字,也会转化成0,这个可以通过自己写转换,或者传入一个int* error,或者返回INT_MIN来设置成功失败。

      接口:

      

#ifdef __cplusplusextern "C" {#endiftypedef struct kv_t {    char* k;    char* v;}kv_t;typedef struct config_t{    kv_t* kv_list;      size_t count;    size_t used;}config_t;/** *@brief : load configure string from char* , *         and parse. string MUST like "key:value" * *@param : [in] configure string  * *@return : configure structure */config_t* ub_config_load(const char*);/** *@brief : get number from spicified key *         *@param : [in] configure structrue to be released *         [out] is error */long ub_config_get_integer(config_t* cfg, const char* key);** *@brief : get CONST char string from spicified key * *@param : configure structrue to be released */const char* ub_config_get_string(config_t* cfg, const char* key);/** *@brief : load configure string from char* , *         and parse. * *@param : configure structrue to be released */void ub_config_release(config_t*);size_t ub_config_used(config_t*);void ub_config_print(config_t*);#ifdef __cplusplus}#endif

      实现的话其实比较简单 , 晒出parse整个字串的代码把 ,其他的先不care :

      

    const char* line = cfg;    const char* cur = cfg;    // iterator char string    while((line-cfg)!=len+1) {        // still in same line        if (*(line)!='\n' && (*line)!='\0') {            line++;            continue;        } else {            // first check the cacpacity            if (config->used >= config->count) {                // extends space                 config->kv_list = (kv_t*)realloc(config->kv_list,sizeof(kv_t)*config->count*2);                if (NULL == config->kv_list)                    goto cleanup;                else {                    config->count *= 2;                }            }            kv_t* kv_pair = &config->kv_list[config->used];            /**             * For Key              */            // skip blank sapce            while(*cur==' ')                cur++;            // ignore the comment (start with "#")            if (*cur == '#' || *cur == '\n' || *cur == '\r')                goto keepgo;            const char* key = cur;            while(*key!=' '&&*key!=':'&&key!=line)                key++;           // alloc len(key) + '\0' space            char* tmp_key = (char*)calloc(1,key-cur+1);            memcpy(tmp_key,cur,key-cur);            int breakout = 0;            while(*cur++!=':') {                if (cur==line) {                    free(tmp_key);                    breakout = 1;                }            }            // don't find value            if (breakout)                goto keepgo;            /**             * For Value             */            // skip blank space after ": "            while(*cur==' ')                cur++;            // oops , bad string            if (cur>=line) {                free(tmp_key);                goto keepgo;            }            const char* value = cur;            while(value<=line) {                if (*(value+1)==' ' || value+1==line)                    break;                else                    value++;            }            char* tmp_value = (char*)calloc(1,value-cur+2);            // don't copy "\n"            memcpy(tmp_value,cur,value-cur+1);            // kv ok            config->used++;            kv_pair->k = tmp_key;            kv_pair->v = tmp_value;keepgo:            line++;            cur = line;        }    }    return config;cleanup :    free(config);check_exception :    return NULL;}

        这个些地方又可以优化的部分,大家可以自己考虑一下,比如放入kv_list数组时候可以有序,这样检索时候可以做二分。或者用Hash,但在小数据量下,Hash的效果并不一定比二分优,反而可能更低。字符切割是自己写的。双指针移动,这里可以考虑省去小部分代码。。。