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

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

      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 __cplusplus
extern "C" {
#endif

typedef 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的效果并不一定比二分优,反而可能更低。字符切割是自己写的。双指针移动,这里可以考虑省去小部分代码。。。

你可能感兴趣的:(linux,linux,linux,.,cc++,configure)