好久没更新博客了哈,今天抽空把之前写了一部分的东西拿出来继续分享。
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
接口:
#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的效果并不一定比二分优,反而可能更低。字符切割是自己写的。双指针移动,这里可以考虑省去小部分代码。。。