1 /****************************************************************************** 2 * 3 * parseConf(配置文件解析器) 4 * 5 * 1. 很多时候,我们安装一些软件,都可以通过改一些软件的配置文件来修改程序的 6 * 运行性能,如Tomcat修改端口号,访问数据库时一些固定的参数等等; 7 * 2. 本Demo就是干着这么一件事,从properties.conf文件中取出键值对(keyvalue), 8 * 实现更大程度提高代码的可利用性,通用性; 9 * 3. 以下是我们要解析的properties.conf文件中的内容: 10 * #title = charMaps 11 * t itle = c harMaps 12 * #jfdalj lasdfjl jflds 13 * jfdsljf 14 * =fjldsfsjd 15 * up = looking 16 * rows = 24 #jals djfaldjfals 17 * r ows = 25 #jals djfaldjfals 18 * c ols = 8 0 19 * 20 * = fsdfa 21 * 22 * c ols = 88 0 23 * jsflsjfd 24 * jfsldjaf 25 * tadjfsldjf= 26 * 27 * cols=88 0 28 * cols=888 0 29 * interval = 1 0000 30 * version = 11.0 31 * lkjk ng = i an f n ig 32 * test = 100000000 33 * 4. 这是我们使用本parseConf程序解析出来的结果: 34 * 001: t itle=c harMaps 35 * 002: up=looking 36 * 003: rows=24 37 * 004: r ows=25 38 * 005: c ols=88 0 39 * 006: cols=888 0 40 * 007: interval=1 0000 41 * 008: version=11.0 42 * 009: lkjk ng=i an f n ig 43 * 010: test=100000000 44 * 5. 配置文件的书写规范: 45 * 1. 键值对(keyvalue)以key=value的形式存在,等号两边可以出现空格; 46 * 2. 对于不能构成键值对(keyvalue)的key或value都会被忽略; 47 * 3. '#'为行注释符,目前只支持单行注释,不提供多行注释; :) 48 * 4. 如果解析中发现键值对中key相同,那么取最后那次的键值对为最终键值对; 49 * 6. 使用valgrind对程序进行内存释放检查结果,不会造成内存泄露: 50 * [user@localhost parseConf]$ valgrind ./parseConf properties.conf 51 * ==6325== Memcheck, a memory error detector 52 * ==6325== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. 53 * ==6325== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info 54 * ==6325== Command: ./parseConf properties.conf 55 * ==6325== 56 * ... //省略程序运行时的输出内容 57 * ==6325== 58 * ==6325== HEAP SUMMARY: 59 * ==6325== in use at exit: 0 bytes in 0 blocks 60 * ==6325== total heap usage: 39 allocs, 39 frees, 9,092 bytes allocated 61 * ==6325== 62 * ==6325== All heap blocks were freed -- no leaks are possible 63 * ==6325== 64 * ==6325== For counts of detected and suppressed errors, rerun with: -v 65 * ==6325== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 12 from 8) 66 * 67 * 2015-3-28 晴 深圳 曾剑锋 68 *****************************************************************************/ 69 70 #include <stdlib.h> 71 #include <stdio.h> 72 #include <string.h> 73 #include <fcntl.h> 74 75 //默认一行数据的缓冲区大小 76 #define BUFFER_SIZE 1024 77 78 //键值对结构体,本Demo采用单链表来实现 79 typedef struct KVPAIR { 80 char key[128]; 81 char value[512]; 82 struct KVPAIR * next; 83 } kvpair; 84 /** 85 * 获取键值对的起始指针,参数是传入需要保存keyvalus首地址的指针, 86 * 函数返回值为0时表示获取成功 87 */ 88 int getkvpairs(char *conffile, kvpair** kvpairs); 89 /** 90 * 通过key值获取kvpairs中的value,如果链表中没有key对应的数据,或者给的参数错误 91 * 将返回NULL 92 */ 93 char* key2val(char* key, kvpair* kvpairs); 94 /** 95 * 通过value值获取kvpairs中的key,如果链表中没有value对应的数据,或者给的参数错误 96 * 将返回NULL 97 */ 98 char* val2key(char* value, kvpair* kvpairs); 99 //打印输出kvpairs中所有的键值对 100 void printkvpairs(kvpair* kvpairs); 101 //用'\0'填充字符串 102 void cleanString(char* string); 103 /** 104 * 查看链表中有没有当前key对应的键值对,如果有,返回该key对应的键值对 105 * 如果没有,将返回NULL 106 */ 107 kvpair* checkKey(char* key, kvpair* kvpairs); 108 //释放链表 109 int freekvpairs(kvpair* kvpairs); 110 //去除字符串左侧不可见字符 111 char *ltrim(char* str); 112 //去除字符串右侧不可见字符 113 char *rtrim(char* str); 114 //去除字符串左右不可见字符 115 char *trim(char* str); 116 117 /** 118 * on success, return 0, otherwise return -1 119 * 120 * 配置文件预处理过程是以一行一行来处理的,大致思路如下: 121 * while(直到文件末尾){ 122 * 1.删除一行中前面的' ','\t'; 123 * 2.忽略掉那些以'\n','#','='开头的行; 124 * 3.如果一行中有'#'注释,将'#'所在的位置设置'\0',代表字符串末尾; 125 * 也就是'#'以及后面注释都不管,因为那是注释 :) 126 * 4.删除一行中末尾的换行符; 127 * 5.修剪获取到的key,value字符串; 128 * 6.剩下的也就是是键值对了,保存在链表中. 129 * } 130 */ 131 int getkvpairs(char* conffile, kvpair** kvpairs){ 132 /** 133 * 如果传入的参数conffile不是NULL,并且配置文件能打开,则使用该文件中的配置参数 134 * 如果conffile指定的文件失效,则使用当前文件夹下的./properties.conf文件作为配置 135 * 文件,如果前面两者都失效,则会报错,并返回-1,文件后缀conf是properties的缩写 136 */ 137 if(kvpairs == NULL){ 138 perror("function( getkvpairs ) parameter ( kvpairs ) was NULL\n"); 139 return -1; 140 } 141 142 if (conffile == NULL) 143 conffile = "./properties.conf"; 144 145 FILE* conf = NULL; 146 conf = fopen(conffile, "r"); 147 if(conf == NULL){ 148 perror("function( getconfpairs ) can't found the properties file\n"); 149 return -1; 150 } 151 152 int i = 0; //用于循环计数 153 int index = 0; //dealWithBuffer数组中作为保存缓存数据的指针 154 int length = 0; //保存字符串的长度 155 int equalIndex = 0; //保存等号的下标 156 kvpair* keyValueHead = NULL; //用于保存键值对的头节点 157 kvpair* currentkvpair = NULL; //用于保存键值对的当前节点 158 kvpair* previewkvpair = NULL; //用于保存键值对的前一个节点 159 char* lineBuffer = calloc(BUFFER_SIZE, sizeof(char)); 160 char* dealWithBuffer = calloc(BUFFER_SIZE, sizeof(char)); 161 162 while(fgets(lineBuffer, BUFFER_SIZE, conf)){ 163 index = 0; 164 equalIndex = 0; 165 length = strlen(lineBuffer); 166 /** 167 * 删除行首的空格,制表符 168 */ 169 for(i = 0; i < length; i++){ 170 if((lineBuffer[i] != ' ') && (lineBuffer[i] != '\t')){ 171 strcpy(dealWithBuffer, &(lineBuffer[i])); 172 break; 173 } 174 } 175 /** 176 * 清除一行中有#来注释的部分,保留键值对 177 * 且找出一行中=所在的位置,位置信息保存在equalIndex中 178 */ 179 length = strlen(dealWithBuffer); 180 for(i = 0; i < length; i++){ 181 if(dealWithBuffer[i] == '#' ){ 182 dealWithBuffer[i++] = '\0'; 183 break; 184 }else if(dealWithBuffer[i] == '=' ){ 185 equalIndex = i; 186 } 187 } 188 /** 189 * 删除以换行符,#,=等字符开始的行,同时清空dealWithBuffer缓冲区 190 */ 191 if((equalIndex == 0) || (lineBuffer[ 0 ] == '\n') || (lineBuffer[ 0 ] == '#')) { 192 /** 193 * 一定要记得清理这个缓存 194 */ 195 cleanString(dealWithBuffer); 196 continue; 197 } 198 /** 199 * 如果一行数据末尾是'\n',则换成'\0',相当于移除'\n' 200 */ 201 length = strlen(dealWithBuffer); 202 if(dealWithBuffer[length-1] == '\n'){ 203 dealWithBuffer[length-1] = '\0'; 204 } 205 /** 206 * 通过将'='换成'\0',这样就key,value字符串 207 */ 208 dealWithBuffer[equalIndex] = '\0'; 209 /** 210 * 一定要的得加1, 因为字符串长度不包括尾零 211 */ 212 char* key = calloc(strlen(dealWithBuffer)+1, sizeof(char)); 213 char* value = calloc(strlen(&(dealWithBuffer[equalIndex+1]))+1, sizeof(char)); 214 strcpy(key, dealWithBuffer); 215 strcpy(value, &(dealWithBuffer[equalIndex+1])); 216 217 /** 218 * 修剪key,value的值,也就是去掉字符串左右两边的' ','\t' 219 */ 220 trim(key); 221 trim(value); 222 /** 223 * 接下来检查key是否存在,如果存在,直接修改其value,而不创建数据结构体 224 * 如果key不存在,则创建结构体,保存key,value,加入链表 225 * 当然,先要保证key,value有效 226 */ 227 if((strlen(key) != 0) && (strlen(value) != 0)){ 228 if((currentkvpair = checkKey(key, keyValueHead)) != NULL){ 229 bzero(currentkvpair->value, strlen(currentkvpair->value)); 230 strcpy(currentkvpair->value, value); 231 }else{ 232 currentkvpair = malloc(sizeof(kvpair)); 233 strcpy(currentkvpair->key, key); 234 strcpy(currentkvpair->value, value); 235 currentkvpair->next = NULL; 236 if(keyValueHead == NULL){ 237 keyValueHead = currentkvpair; 238 previewkvpair = currentkvpair; 239 }else { 240 previewkvpair->next = currentkvpair; 241 previewkvpair = currentkvpair; 242 currentkvpair = NULL; 243 } 244 } 245 } 246 247 bzero(dealWithBuffer, BUFFER_SIZE);//不能使用cleanString清理,因为字符中间有'\0' 248 cleanString(lineBuffer); 249 free(key); 250 free(value); 251 } 252 free(lineBuffer); 253 free(dealWithBuffer); 254 *kvpairs = keyValueHead; 255 fclose(conf); 256 return 0; 257 } 258 259 void cleanString(char* string){ 260 int i; 261 int length = strlen(string); 262 for(i = 0; i < length; i++){ 263 string[i] = '\0'; 264 } 265 } 266 267 char* key2val(char* key, kvpair* kvpairs){ 268 if((key == NULL) || (strlen(key) == 0)){ 269 perror("function( key2val) parameter ( key ) was NULL\n"); 270 return NULL; 271 } 272 273 kvpair* currentkvpair = kvpairs; 274 while(currentkvpair){ 275 /** 276 * 本来打算直接用strcmp,但是貌似strcmp会自动比较字符串所占数组的大小 277 * 所以改成使用strncmp 278 */ 279 if(strncmp(currentkvpair->key, key, strlen(key)) == 0){ 280 return currentkvpair->value; 281 } 282 currentkvpair = currentkvpair->next; 283 } 284 return NULL; 285 } 286 287 char* val2key(char* value, kvpair* kvpairs){ 288 if((value == NULL) || (strlen(value) == 0)){ 289 perror("function( val2key) parameter ( value ) was NULL\n"); 290 return NULL; 291 } 292 293 kvpair* currentkvpair = kvpairs; 294 while(currentkvpair){ 295 if(strncmp(currentkvpair->value, value, strlen(value)) == 0){ 296 return currentkvpair->key; 297 } 298 currentkvpair = currentkvpair->next; 299 } 300 return NULL; 301 } 302 303 kvpair* checkKey(char* key, kvpair* kvpairs){ 304 if((key == NULL) || (strlen(key) == 0)){ 305 perror("function( checkKey ) parameter ( key ) was NULL\n"); 306 return NULL; 307 } 308 309 kvpair* currentkvpair = kvpairs; 310 while(currentkvpair){ 311 if(strncmp(currentkvpair->key, key, strlen(key)) == 0){ 312 return currentkvpair; 313 } 314 currentkvpair = currentkvpair->next; 315 } 316 return NULL; 317 } 318 319 void printkvpairs(kvpair* kvpairs){ 320 if(kvpairs == NULL){ 321 perror("function( printkvpairs ) parameter( kvpairs ) was NULL\n"); 322 return; 323 } 324 325 int index = 1; 326 kvpair* currentkvpair = kvpairs; 327 printf("\033[32m--------------------------------------\033[0m\n"); 328 while(currentkvpair){ 329 printf("\033[32m %03d: %s=%s\033[0m\n", index, currentkvpair->key, currentkvpair->value); 330 currentkvpair = currentkvpair->next; 331 index++; 332 } 333 printf("\033[32m--------------------------------------\033[0m\n"); 334 } 335 336 int freekvpairs(kvpair* kvpairs){ 337 if(kvpairs == NULL){ 338 return 0; 339 } 340 341 kvpair* previewkvpair = kvpairs; 342 kvpair* currentkvpair = kvpairs; 343 while(currentkvpair->next){ 344 previewkvpair = currentkvpair; 345 currentkvpair = currentkvpair->next; 346 free(previewkvpair); 347 } 348 free(currentkvpair); 349 return 0; 350 } 351 352 char *ltrim(char* str) { 353 char str_tmp[BUFFER_SIZE] = {0}; 354 char *current = str_tmp; 355 int count = 0; 356 357 strncpy(str_tmp, str, strlen(str)); 358 bzero(str, strlen(str)); 359 360 while(' ' == (*current) || ('\t' == *current )) 361 current++; 362 363 strncpy(str, current, strlen(current)); 364 return str; 365 } 366 367 char *rtrim(char* str) { 368 int count = 0; 369 int i = strlen(str)-1; 370 for(; i >= 0; i--){ 371 if((' ' == str[i]) || ('\t' == str[i]) || ('\0' == str[i])) 372 str[i] = '\0'; 373 else 374 break; 375 } 376 return str; 377 } 378 379 char *trim(char* str) { 380 return rtrim(ltrim(str)); 381 } 382 383 int main(int argc, char* argv[]){ 384 //传入需要被解析的文件 385 if(argc < 2){ 386 printf(" Usage:\n\r ./parseConf <configure file> \n\n"); 387 return ; 388 } 389 390 /** 391 * 获取键值对,键值对头节点保存在keyValues中 392 */ 393 kvpair* keyValues; 394 getkvpairs(argv[1], &keyValues); 395 printf("\n\033[32m\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\033[36mDemo\033[32m/////////////////\033[0m\n"); 396 397 /** 398 * 将配置文件中的内容打印出来 399 */ 400 int fd = open(argv[1], O_RDONLY); 401 if(-1 == fd){ 402 perror("open file error"); 403 } 404 405 char buffer[1024] = {0}; 406 read(fd, buffer, 1024); 407 printf("%s\n", buffer); 408 409 close(fd); 410 411 /** 412 * 将当前的所有的键值对打印出来 413 */ 414 printkvpairs(keyValues); 415 /** 416 * 通过key获取value值 417 */ 418 char* key = "rows"; 419 printf("\033[32mgetValueBykey:key = %s; value = %s\033[0m\n", key, key2val(key, keyValues)); 420 /** 421 * 通过value获取key值 422 */ 423 char* value = "24"; 424 printf("\033[32mgetKeyByValue:value = %s; key = %s\033[0m\n", value, val2key(value, keyValues)); 425 printf("\033[32m--------------------------------------\033[0m\n"); 426 /** 427 * 释放keyValues链表 428 */ 429 if(freekvpairs(keyValues) == 0){ 430 printf("\033[32m Memory of keyValues linked has freed\033[0m\n"); 431 printf("\033[32m--------------------------------------\033[0m\n"); 432 } 433 }