parseConf(配置文件解析器)
/******************************************************************************
*
* parseConf(配置文件解析器)
*
* 1. 很多时候,我们安装一些软件,都可以通过改一些软件的配置文件来修改程序的
* 运行性能,如Tomcat修改端口号,访问数据库时一些固定的参数等等;
* 2. 本Demo就是干着这么一件事,从properties.conf文件中取出键值对(keyvalue),
* 实现更大程度提高代码的可利用性,通用性;
* 3. 以下是我们要解析的properties.conf文件中的内容:
* #title = charMaps
* t itle = c harMaps
* #jfdalj lasdfjl jflds
* jfdsljf
* =fjldsfsjd
* up = looking
* rows = 24 #jals djfaldjfals
* r ows = 25 #jals djfaldjfals
* c ols = 8 0
*
* = fsdfa
*
* c ols = 88 0
* jsflsjfd
* jfsldjaf
* tadjfsldjf=
*
* cols=88 0
* cols=888 0
* interval = 1 0000
* version = 11.0
* lkjk ng = i an f n ig
* test = 100000000
* 4. 这是我们使用本parseConf程序解析出来的结果:
* 001: t itle=c harMaps
* 002: up=looking
* 003: rows=24
* 004: r ows=25
* 005: c ols=88 0
* 006: cols=888 0
* 007: interval=1 0000
* 008: version=11.0
* 009: lkjk ng=i an f n ig
* 010: test=100000000
* 5. 配置文件的书写规范:
* 1. 键值对(keyvalue)以key=value的形式存在,等号两边可以出现空格;
* 2. 对于不能构成键值对(keyvalue)的key或value都会被忽略;
* 3. '#'为行注释符,目前只支持单行注释,不提供多行注释; :)
* 4. 如果解析中发现键值对中key相同,那么取最后那次的键值对为最终键值对;
* 6. 使用valgrind对程序进行内存释放检查结果,不会造成内存泄露:
* [user@localhost parseConf]$ valgrind ./parseConf properties.conf
* ==6325== Memcheck, a memory error detector
* ==6325== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
* ==6325== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
* ==6325== Command: ./parseConf properties.conf
* ==6325==
* ... //省略程序运行时的输出内容
* ==6325==
* ==6325== HEAP SUMMARY:
* ==6325== in use at exit: 0 bytes in 0 blocks
* ==6325== total heap usage: 39 allocs, 39 frees, 9,092 bytes allocated
* ==6325==
* ==6325== All heap blocks were freed -- no leaks are possible
* ==6325==
* ==6325== For counts of detected and suppressed errors, rerun with: -v
* ==6325== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 12 from 8)
*
* 2015-3-28 晴 深圳 曾剑锋
*****************************************************************************/ #include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h> //默认一行数据的缓冲区大小
#define BUFFER_SIZE 1024 //键值对结构体,本Demo采用单链表来实现
typedef struct KVPAIR {
char key[];
char value[];
struct KVPAIR * next;
} kvpair;
/**
* 获取键值对的起始指针,参数是传入需要保存keyvalus首地址的指针,
* 函数返回值为0时表示获取成功
*/
int getkvpairs(char *conffile, kvpair** kvpairs);
/**
* 通过key值获取kvpairs中的value,如果链表中没有key对应的数据,或者给的参数错误
* 将返回NULL
*/
char* key2val(char* key, kvpair* kvpairs);
/**
* 通过value值获取kvpairs中的key,如果链表中没有value对应的数据,或者给的参数错误
* 将返回NULL
*/
char* val2key(char* value, kvpair* kvpairs);
//打印输出kvpairs中所有的键值对
void printkvpairs(kvpair* kvpairs);
//用'\0'填充字符串
void cleanString(char* string);
/**
* 查看链表中有没有当前key对应的键值对,如果有,返回该key对应的键值对
* 如果没有,将返回NULL
*/
kvpair* checkKey(char* key, kvpair* kvpairs);
//释放链表
int freekvpairs(kvpair* kvpairs);
//去除字符串左侧不可见字符
char *ltrim(char* str);
//去除字符串右侧不可见字符
char *rtrim(char* str);
//去除字符串左右不可见字符
char *trim(char* str); /**
* on success, return 0, otherwise return -1
*
* 配置文件预处理过程是以一行一行来处理的,大致思路如下:
* while(直到文件末尾){
* 1.删除一行中前面的' ','\t';
* 2.忽略掉那些以'\n','#','='开头的行;
* 3.如果一行中有'#'注释,将'#'所在的位置设置'\0',代表字符串末尾;
* 也就是'#'以及后面注释都不管,因为那是注释 :)
* 4.删除一行中末尾的换行符;
* 5.修剪获取到的key,value字符串;
* 6.剩下的也就是是键值对了,保存在链表中.
* }
*/
int getkvpairs(char* conffile, kvpair** kvpairs){
/**
* 如果传入的参数conffile不是NULL,并且配置文件能打开,则使用该文件中的配置参数
* 如果conffile指定的文件失效,则使用当前文件夹下的./properties.conf文件作为配置
* 文件,如果前面两者都失效,则会报错,并返回-1,文件后缀conf是properties的缩写
*/
if(kvpairs == NULL){
perror("function( getkvpairs ) parameter ( kvpairs ) was NULL\n");
return -;
} if (conffile == NULL)
conffile = "./properties.conf"; FILE* conf = NULL;
conf = fopen(conffile, "r");
if(conf == NULL){
perror("function( getconfpairs ) can't found the properties file\n");
return -;
} int i = ; //用于循环计数
int index = ; //dealWithBuffer数组中作为保存缓存数据的指针
int length = ; //保存字符串的长度
int equalIndex = ; //保存等号的下标
kvpair* keyValueHead = NULL; //用于保存键值对的头节点
kvpair* currentkvpair = NULL; //用于保存键值对的当前节点
kvpair* previewkvpair = NULL; //用于保存键值对的前一个节点
char* lineBuffer = calloc(BUFFER_SIZE, sizeof(char));
char* dealWithBuffer = calloc(BUFFER_SIZE, sizeof(char)); while(fgets(lineBuffer, BUFFER_SIZE, conf)){
index = ;
equalIndex = ;
length = strlen(lineBuffer);
/**
* 删除行首的空格,制表符
*/
for(i = ; i < length; i++){
if((lineBuffer[i] != ' ') && (lineBuffer[i] != '\t')){
strcpy(dealWithBuffer, &(lineBuffer[i]));
break;
}
}
/**
* 清除一行中有#来注释的部分,保留键值对
* 且找出一行中=所在的位置,位置信息保存在equalIndex中
*/
length = strlen(dealWithBuffer);
for(i = ; i < length; i++){
if(dealWithBuffer[i] == '#' ){
dealWithBuffer[i++] = '\0';
break;
}else if(dealWithBuffer[i] == '=' ){
equalIndex = i;
}
}
/**
* 删除以换行符,#,=等字符开始的行,同时清空dealWithBuffer缓冲区
*/
if((equalIndex == ) || (lineBuffer[ ] == '\n') || (lineBuffer[ ] == '#')) {
/**
* 一定要记得清理这个缓存
*/
cleanString(dealWithBuffer);
continue;
}
/**
* 如果一行数据末尾是'\n',则换成'\0',相当于移除'\n'
*/
length = strlen(dealWithBuffer);
if(dealWithBuffer[length-] == '\n'){
dealWithBuffer[length-] = '\0';
}
/**
* 通过将'='换成'\0',这样就key,value字符串
*/
dealWithBuffer[equalIndex] = '\0';
/**
* 一定要的得加1, 因为字符串长度不包括尾零
*/
char* key = calloc(strlen(dealWithBuffer)+, sizeof(char));
char* value = calloc(strlen(&(dealWithBuffer[equalIndex+]))+, sizeof(char));
strcpy(key, dealWithBuffer);
strcpy(value, &(dealWithBuffer[equalIndex+])); /**
* 修剪key,value的值,也就是去掉字符串左右两边的' ','\t'
*/
trim(key);
trim(value);
/**
* 接下来检查key是否存在,如果存在,直接修改其value,而不创建数据结构体
* 如果key不存在,则创建结构体,保存key,value,加入链表
* 当然,先要保证key,value有效
*/
if((strlen(key) != ) && (strlen(value) != )){
if((currentkvpair = checkKey(key, keyValueHead)) != NULL){
bzero(currentkvpair->value, strlen(currentkvpair->value));
strcpy(currentkvpair->value, value);
}else{
currentkvpair = malloc(sizeof(kvpair));
strcpy(currentkvpair->key, key);
strcpy(currentkvpair->value, value);
currentkvpair->next = NULL;
if(keyValueHead == NULL){
keyValueHead = currentkvpair;
previewkvpair = currentkvpair;
}else {
previewkvpair->next = currentkvpair;
previewkvpair = currentkvpair;
currentkvpair = NULL;
}
}
} bzero(dealWithBuffer, BUFFER_SIZE);//不能使用cleanString清理,因为字符中间有'\0'
cleanString(lineBuffer);
free(key);
free(value);
}
free(lineBuffer);
free(dealWithBuffer);
*kvpairs = keyValueHead;
fclose(conf);
return ;
} void cleanString(char* string){
int i;
int length = strlen(string);
for(i = ; i < length; i++){
string[i] = '\0';
}
} char* key2val(char* key, kvpair* kvpairs){
if((key == NULL) || (strlen(key) == )){
perror("function( key2val) parameter ( key ) was NULL\n");
return NULL;
} kvpair* currentkvpair = kvpairs;
while(currentkvpair){
/**
* 本来打算直接用strcmp,但是貌似strcmp会自动比较字符串所占数组的大小
* 所以改成使用strncmp
*/
if(strncmp(currentkvpair->key, key, strlen(key)) == ){
return currentkvpair->value;
}
currentkvpair = currentkvpair->next;
}
return NULL;
} char* val2key(char* value, kvpair* kvpairs){
if((value == NULL) || (strlen(value) == )){
perror("function( val2key) parameter ( value ) was NULL\n");
return NULL;
} kvpair* currentkvpair = kvpairs;
while(currentkvpair){
if(strncmp(currentkvpair->value, value, strlen(value)) == ){
return currentkvpair->key;
}
currentkvpair = currentkvpair->next;
}
return NULL;
} kvpair* checkKey(char* key, kvpair* kvpairs){
if((key == NULL) || (strlen(key) == )){
perror("function( checkKey ) parameter ( key ) was NULL\n");
return NULL;
} kvpair* currentkvpair = kvpairs;
while(currentkvpair){
if(strncmp(currentkvpair->key, key, strlen(key)) == ){
return currentkvpair;
}
currentkvpair = currentkvpair->next;
}
return NULL;
} void printkvpairs(kvpair* kvpairs){
if(kvpairs == NULL){
perror("function( printkvpairs ) parameter( kvpairs ) was NULL\n");
return;
} int index = ;
kvpair* currentkvpair = kvpairs;
printf("\033[32m--------------------------------------\033[0m\n");
while(currentkvpair){
printf("\033[32m %03d: %s=%s\033[0m\n", index, currentkvpair->key, currentkvpair->value);
currentkvpair = currentkvpair->next;
index++;
}
printf("\033[32m--------------------------------------\033[0m\n");
} int freekvpairs(kvpair* kvpairs){
if(kvpairs == NULL){
return ;
} kvpair* previewkvpair = kvpairs;
kvpair* currentkvpair = kvpairs;
while(currentkvpair->next){
previewkvpair = currentkvpair;
currentkvpair = currentkvpair->next;
free(previewkvpair);
}
free(currentkvpair);
return ;
} char *ltrim(char* str) {
char str_tmp[BUFFER_SIZE] = {};
char *current = str_tmp;
int count = ; strncpy(str_tmp, str, strlen(str));
bzero(str, strlen(str)); while(' ' == (*current) || ('\t' == *current ))
current++; strncpy(str, current, strlen(current));
return str;
} char *rtrim(char* str) {
int count = ;
int i = strlen(str)-;
for(; i >= ; i--){
if((' ' == str[i]) || ('\t' == str[i]) || ('\0' == str[i]))
str[i] = '\0';
else
break;
}
return str;
} char *trim(char* str) {
return rtrim(ltrim(str));
} int main(int argc, char* argv[]){
//传入需要被解析的文件
if(argc < ){
printf(" Usage:\n\r ./parseConf <configure file> \n\n");
return ;
} /**
* 获取键值对,键值对头节点保存在keyValues中
*/
kvpair* keyValues;
getkvpairs(argv[], &keyValues);
printf("\n\033[32m\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\033[36mDemo\033[32m/////////////////\033[0m\n"); /**
* 将配置文件中的内容打印出来
*/
int fd = open(argv[], O_RDONLY);
if(- == fd){
perror("open file error");
} char buffer[] = {};
read(fd, buffer, );
printf("%s\n", buffer); close(fd); /**
* 将当前的所有的键值对打印出来
*/
printkvpairs(keyValues);
/**
* 通过key获取value值
*/
char* key = "rows";
printf("\033[32mgetValueBykey:key = %s; value = %s\033[0m\n", key, key2val(key, keyValues));
/**
* 通过value获取key值
*/
char* value = "";
printf("\033[32mgetKeyByValue:value = %s; key = %s\033[0m\n", value, val2key(value, keyValues));
printf("\033[32m--------------------------------------\033[0m\n");
/**
* 释放keyValues链表
*/
if(freekvpairs(keyValues) == ){
printf("\033[32m Memory of keyValues linked has freed\033[0m\n");
printf("\033[32m--------------------------------------\033[0m\n");
}
}
parseConf(配置文件解析器)的更多相关文章
- golang开发:类库篇(四)配置文件解析器goconfig的使用
为什么要使用goconfig解析配置文件 目前各语言框架对配置文件书写基本都差不多,基本都是首先配置一些基础变量,基本变量里面有环境的配置,然后通过环境变量去获取该环境下的变量.例如,生产环境跟测试环 ...
- python接口自动化测试 - configparser配置文件解析器详细使用
configparser简介 ConfigParser模块已在Python 3中重命名为configparser 该模块定义了ConfigParser类. ConfigParser类实现一种基本的配置 ...
- Python3-configparser模块-配置文件解析器
Python3中的configparser模块主要用于处理类似于windows ini 文件结构的配置文件 1.configparser模块提供实现基本配置语言的ConfigParser类 2.配置文 ...
- Python标准组件ConfigParser配置文件解析器,保存配置时支持大写字母的方法
虽然自己已经改用xml作为配置文件首选格式了,但是有时候还是需要解析ini.cfg文件(为了兼容早期版本或者其他作者的软件). 基本上Python自带的ConfigParser足够应对了,但是美中不足 ...
- Springboot配置文件解析器
@EnableScheduling @MapperScan(value = "com.****.dao") @EnableTransactionManagement @Enable ...
- Python模块:配置文件解析器configparser
版权声明:本文为博主皮皮http://blog.csdn.net/pipisorry原创文章,未经博主同意不得转载. https://blog.csdn.net/pipisorry/article/d ...
- python configparser配置文件解析器
一.Configparser 此模块提供实现基本配置语言的ConfigParser类,该语言提供类似于Microsoft Windows INI文件中的结构.我们经常会在一些软件安装目录下看到.ini ...
- python模块----configpaser (key:value型 配置文件解析器)
configparser是用来读取配置文件的包,配置文件的格式类似:[section]+内容(键=值) 标准库网址:https://docs.python.org/3/library/configpa ...
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
随机推荐
- shell 命令行光标跳转快捷键和history的用法
Ctrl+a: 跳到命令行首 Ctrl+e: 跳到命令行尾 Ctrl+u: 删除光标至命令行首的内容 Ctrl+k: 删除光标至命令行尾的内容 Ctrl+<- 跳到前一个单词首部 Ctrl+-& ...
- 一个很棒的Flutter学习资源列表
目录 文章 一开始 HOWTO文档 网站/博客 高级 视频 组件 演示 UI 材料设计 图片 地图 图表 导航 验证 文字和富文本 分析.流量统计 自动构建 风格样式 媒体 音频 视频 语音 存储 获 ...
- Java实现冒泡排序算法
一.基本思路: 冒泡排序是一种简单的交换类排序.其基本思路是,从头开始扫描待排序的元素,在扫描过程中依次对相邻元素进行比较,将关键字值大的元素后移.每经过 一趟排序后,关键字值最大的元素将移到末尾,此 ...
- Educational Codeforces Round 47 (Rated for Div. 2)F. Dominant Indices 线段树合并
题意:有一棵树,对于每个点求子树中离他深度最多的深度是多少, 题解:线段树合并快如闪电,每个节点开一个权值线段树,递归时合并即可,然后维护区间最多的是哪个权值,到x的深度就是到根的深度减去x到根的深度 ...
- python-day53--前端js
一.基本语法(ECMA) 单行注释 // /* 多行注释 */ 变量赋值 默认以换行符作为结束符,有分好以分号作为结束符号 JS的引入方式: 1. <script> </script ...
- HDOJ1009
#include "iostream" #include "algorithm" #include "cstdio" using names ...
- nodejs初探一二
概念 简单来说,node.js 是一个让 JavaScript 运行在服务端的开发平台,让开发者通过JS编写服务端程序. 安装 从nodeJS官网下载对应平台的安装程序,安装完成后,打开命令行工具,然 ...
- Py打包exe报错
Py打包exe报错 下载地址 https://github.com/pyinstaller/pyinstaller 用管理员执行 pip install https:/ ...
- 微信小程序wx.chooseImage和wx.previewImage的综合使用(图片上传可以限制个数)
本例从微信小程序的组件扒下来的. WXML: <view class="weui-cell"> <view class="weui-cell__bd&q ...
- linux make virtual memory more efficient three components
Page Cache This is used to speed up access to images and data on disk. As pages are read into memory ...