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 //用' '填充字符串 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.删除一行中前面的' ',' '; 123 * 2.忽略掉那些以' ','#','='开头的行; 124 * 3.如果一行中有'#'注释,将'#'所在的位置设置' ',代表字符串末尾; 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 "); 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 "); 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] != ' ')){ 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++] = '