Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1682872
  • 博文数量: 311
  • 博客积分: 7778
  • 博客等级: 少将
  • 技术积分: 4186
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-09 19:59
个人简介

蓝点工坊(http://www.bluedrum.cn) 创始人,App和嵌入式产品开发。同时也做相应培训和外包工作。 详细介绍 http://pan.baidu.com/s/1y2g88

文章存档

2012年(3)

2011年(115)

2010年(170)

2009年(23)

分类: C/C++

2010-09-28 15:11:30

Andrew Huang
 
无论是在桌面机或者嵌入式应用里,用配置文件来保存不同参数是很常见的应用。如果用C语言来处理配置文件,是一个很实际的应用。
  假设配置文件有如下格式,
  • #打头的行为注释行
  •  一个参数占一行,参数格式如下  param = value
  • 参数值可以为空格 即 param =
  • 每一行不超过1024字符。
以下是在课堂写的配置文件读写模块,可以直接用于工作。
 
主要使用函数指针,大量用到字符串解析,这里开发者调用者可以通过定义不同static CONF_ITEM __conf_list[]来实现读不同配置参数
 
配置模块头文件,

/* Andrew Huang */

#ifndef __CONF_FILE_H__
#define __CONF_FILE_H__

#ifdef __cplusplus
extern "C" {
#endif

#define CL_TYPE_UNKNOW 0x00 /* 无法识别的行 */
#define CL_TYPE_COMMENT 0x01 /* 注释*/
#define CL_TYPE_CONFIGURE 0x02 /* 配置项行*/
#define CL_TYPE_EMPTY 0x03 /* 空行*/
#define CL_TYPE_UNSUPPORT 0x04 /* 不支持的变量*/

//enum 相当于取值范围受限的一个整数


typedef enum ci_val_type {
   ci_integer = 0,
   ci_string = 2,
   ci_ipaddr = 4,
}CI_VAL_TYPE;


typedef struct conf_item {
  char * ci_name; /* 配置项名字*/
  int ci_val_type ; /* 配置的类型,整数,字符串,IP地址 ... */
  char * ci_default ; /* 配置默认值 */
  char * ci_value ; /* 配置当前值*/
  int (* ci_handle)(char * name,char * value,void * data);
  
}CONF_ITEM;

extern int ci_parse_ipaddr(char * name,char * value,void * data);
extern int ci_parse_string(char * name,char * value,void * data);
extern int ci_parse_integer(char * name,char * value,void * data);

#ifdef __cplusplus
}
#endif

#endif

 
配置模块读写程序
 

#include <stdio.h>
#include <ctype.h>
#include "conf_file.h"


static CONF_ITEM * find_conf_item(CONF_ITEM * p_list,char * name)
{
   CONF_ITEM * p = NULL;
   
   for(p = p_list ; p->ci_name != NULL ; p++)
   {
      if(strcmp(name,p->ci_name) == 0)
        return p;
   }
   
   
   fprintf(stderr,"ERROR: unknow var %s\n",name);
   
     return NULL; /* 不支持变量名 */
   
   
}


static int parse_conf_line(CONF_ITEM * p_list,int line_no,char * line)
{
   char * p = line;
   char * val ,* name;
      
   
   CONF_ITEM * p_item ;

   
//去掉首部空格

   for(p ; *p == ' ' ; p++);
   
   if(*p == '#')
     return CL_TYPE_COMMENT;
   else if(*p == '\n')
      return CL_TYPE_EMPTY;
   
   
//查找=,并将其置为0,这样简单折分出变量名和变量值来

   name = p;
   for(; (*p!='=') && (*p!='\n') ; p++)
     {
       *p = tolower(*p);
//顺便把变量名变成小写

     }
   
   if(*p != '=')
//表示这一行没有 =

   {
      return CL_TYPE_UNKNOW;
   }
   
   
   val = p+1;
   p--;
   /* 截掉变量名后面空格*/
   for(; *p==' ' ; p--);
     
   *(p+1) = 0;
//截断前后两段

      
   
   
   
   
//去掉值前的空格

   for( ; *val == ' ' ; val++);
   
   
//去掉尾部的回车符

   for( p = val; *p != '\n' ; p++);
   
   if(*p == '\n')
     *p = 0;
   
   
   
//查找name 是不是系统支持的配置项

   p_item = find_conf_item(p_list,name);
   
   if(p_item == NULL)
     return CL_TYPE_UNSUPPORT;
    
     
   p_item->ci_handle(name,val,p_item);
   
   return CL_TYPE_CONFIGURE;
   

}
 
/* 配置文件*/
int init_conf_file()
{
  /* 练习:自动把所有变量值赋成缺省值,用malloc分配空间*/
  /* 如果是 ci_integer ,要分配一个整数,*/
}

#define MAX_CONF_LINE_LEN 1024

int parse_conf_file(CONF_ITEM * p_list,char * filename)
{
  FILE * fp = NULL;
  char buffer[MAX_CONF_LINE_LEN];
  
  int line_no = 0;
  int type;
  
  
  
  
//以只读的方式打开一个文本文件

  fp = fopen(filename,"r");
  if(fp == NULL)
  {
    fprintf(stderr,"open file %s failure \n",filename);
    return -1;
  }
 
  while( fgets(buffer,sizeof(buffer),fp) != NULL)
  {
     
//解释配置文件行;

     type = parse_conf_line(p_list,++line_no,buffer);
     switch(type)
     {
     default:
     case CL_TYPE_UNKNOW:
       fprintf(stderr,"line %d:unknow %s\n",line_no,buffer);
       break;
     case CL_TYPE_EMPTY:
       fprintf(stderr,"line %d:empty line\n",line_no);
       break;
     case CL_TYPE_COMMENT:
       fprintf(stderr,"line %d:comment %s\n",line_no,buffer);
       break;
      case CL_TYPE_CONFIGURE:
       fprintf(stderr,"line %d:configure %s\n",line_no,buffer);
       break;
      case CL_TYPE_UNSUPPORT:
       fprintf(stderr,"line %d:unsupport %s\n",line_no,buffer);
       break;

  
     }
  }
  
   
  fclose(fp);
  
}


 int ci_parse_ipaddr(char * name,char * value,void * data)
{
   
//把value 赋给 p_item->value ...

   
   printf("%s:name=\"%s\",value=\"%s\"\n",__FUNCTION__,name,value);
   
   return 0;
}
 int ci_parse_string(char * name,char * value,void * data)
{
  printf("%s:name=\"%s\",value=\"%s\"\n",__FUNCTION__,name,value);
  return 0;
}
 int ci_parse_integer(char * name,char * value,void * data)
{
    printf("%s:name=\"%s\",value=\"%s\"\n",__FUNCTION__,name,value);
 return 0;
}

 
测试主程序
 

/*
 

  Andrew Huang
   
  操作配置文件的代码
  1.有注释,以#打头,行首空格要忽略
  2.配置项格式
      变量名 = 变量值
      变量名= 也合法.=两边有无空格不影响结果
  
  3.写程序的人一定知道他要读取哪一些变量,以及变量的含义和格式
  4.没有出现变量自动采用默认值
  5.变量前后顺序不影响最终结果。即可一个配置变量可以调整顺序.
  
  
  #this is a commant
  ipaddr = 192.168.0.1
  host=developer.com
  
  有限分段的设计com1-com8
  [com1]
  baudrate=115200 //--> com1_baudrate=...
  stopbit = 1 //--> com1_stopbit=
  
  
  [com2]
  baudrate=9600
  stopbit=2
  ----------------
  无限分段,用户帐号为一段
  [9060]
  ...
  [9601]
  ...
  
  
*/


/*
扩展练习:1.增加段支持 [network]
          2.变量归属于某一个段的
          3.增加一个变量值格式检查,比如IP的格式
          #3.没有属于某段的变量自动变成全局
*/


#include <stdio.h>


#include <conf_file.h>


/* 配置项列表 */
static CONF_ITEM __conf_list[] = {
  {"ipaddr", ci_ipaddr, "192.168.1.1",NULL,ci_parse_ipaddr },
  {"host", ci_string, "hxy" ,NULL,ci_parse_string },
  {"port", ci_integer, "11234" ,NULL,ci_parse_integer },
  {"level", ci_integer, "1" ,NULL,ci_parse_integer },
  {NULL, 0    ,    0 ,NULL,NULL }, //结束标志

};


int main()
{
#ifdef DEBUG
  printf("this is a Debug version\n");
#else
  printf("this is a release version\n");
#endif
   parse_conf_file(__conf_list,"my.ini");
   
   return 0;
}


文件: test_conf.zip
大小: 3KB
下载: 下载


阅读(4579) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2011-01-09 10:39:26

在黄老师的博客总能有新的收获。谢谢黄老师啦!