linux下c读写ini配置_linux c 配置文件读取和保存 ini-程序员宅基地

转载至:https://blog.csdn.net/niha1993825jian/article/details/41086403

#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
 
#define  MAX_VALUE  64 /* 定义section,key,value字符串最大长度 */
// printf("File = %s\nLine = %d\nFunc=%s\nDate=%s\nTime=%s\n", __FILE__, __LINE__, __FUNCTION__, __DATE__, __TIME__);
#define  PRINT_ERRMSG(STR) fprintf(stderr,"line:%d,msg:%s,eMsg:%s\n", __LINE__, STR, strerror(errno))
 
typedef struct _option {
  char    key[MAX_VALUE];   /* 对应键 */
  char    value[MAX_VALUE]; /* 对应值 */
  struct  _option *next;    /* 链表连接标识 */
}Option;
 
typedef struct _data {
  char    section[MAX_VALUE]; /* 保存section值          */
  Option  *option;            /* option链表头           */
  struct  _data *next;        /* 链表连接标识           */
}Data;
 
typedef struct {
  char    comment;              /* 表示注释的符号    */
  char    separator;            /* 表示分隔符        */
  char    re_string[MAX_VALUE]; /* 返回值字符串的值  */
  int     re_int;               /* 返回int的值       */
  bool    re_bool;              /* 返回bool的值      */
  double  re_double ;           /* 返回double类型    */
  Data    *data;                /* 保存数据的头      */
}Config;
 
/**
* 判断字符串是否为空
* 为空返回true,不为空返回false
**/
bool str_empty(const char *string)
{
  return NULL == string || 0 == strlen(string);
}
 
/**
* 向链表添加section,key,value
* 如果添加时不存在section则新增一个
* 如果对应section的key不存在则新增一个
* 如果section已存在则不会重复创建
* 如果对应section的key已存在则只会覆盖key的值
**/
bool cnf_add_option(Config *cnf, const char *section, const char *key, const char *value)
{
  if (NULL == cnf || str_empty(section) || str_empty(key) || str_empty(value)) {
    return false; /* 参数不正确,返回false */
  }
  
  Data *p = cnf->data; /* 让变量p循环遍历data,找到对应section */
  while (NULL != p && 0 != strcmp(p->section, section)) {
    p = p->next;
  }
  
  if (NULL == p) { /* 说明没有找到section,需要加一个 */
    Data *ps = (Data*)malloc(sizeof(Data));
    if (NULL == ps) {
      exit(-1); /* 申请内存错误 */
    }
    strcpy(ps->section, section);
    ps->option = NULL;    /* 初始的option要为空 */
    ps->next = cnf->data; /* cnf->data可能为NULL */
    cnf->data = p = ps;   /* 头插法插入链表 */
  }
  
  Option *q = p->option;
  while (NULL != q && 0 != strcmp(q->key, key)) {
    q = q->next; /* 遍历option,检查key是否已经存在 */
  }
  
  if (NULL == q) { /* 不存在option,则新建一个 */
    q = (Option*)malloc(sizeof(Option));
    if (NULL == q) {
      exit(-1); /* 申请内存错误 */
    }
    strcpy(q->key, key);
    q->next = p->option; /*这里p->option可能为NULL,不过也没关系 */
    p->option = q; /* 头插法插入链表 */
  }
  strcpy(q->value, value); /* 无论如何要把值改了 */
  
  return true;
}
 
/**
* 去掉字符串内所有空白
* 且忽略注释部分
* 最终得到没有空白的字符串
**/
bool strip_comments(char *string, char comment)
{
  if (NULL == string || '\n' == *string || '\r' == *string) {
    return false; /* 第一个字符为回车或换行,表示空行 */
  }
  
  char *p, *q; /* 下面去掉字符串中所有空白字符 */
  for (p = q = string; *p != '\0' && *p != comment; p++) {
    if (0 == isspace(*p)) {
      *q++ = *p; /* 不是空白字符则重写字符串 */
    }
  }
  *q = '\0';
  
  return 0 != strlen(string); /* 字符串长度不为0,表示数据可用 */
}
 
/**
* 传递配置文件路径
* 参数有文件路径,注释字符,分隔符
* 返回Config结构体
**/
Config *cnf_read_config(const char *filename, char comment, char separator)
{
  Config *cnf     = (Config*)malloc(sizeof(Config));
  cnf->comment    = comment; /* 每一行该字符及以后的字符将丢弃 */
  cnf->separator  = separator; /* 用来分隔Section 和 数据 */
  cnf->data       = NULL; /* 初始数据为空 */
  
  if (str_empty(filename)) {
    return cnf; /* 空字符串则直接返回对象 */
  }
  
  char *p, sLine[MAX_VALUE];    /* 保存一行数据到字符串 */
  char section[MAX_VALUE], key[MAX_VALUE], value[MAX_VALUE]; /* 缓存section,key,value */
  FILE *fp = fopen(filename, "r");
  if(NULL == fp) {
    PRINT_ERRMSG("fopen");
    exit(errno); /* 读文件错误直接按照错误码退出 */
  }
  
  while (NULL != fgets(sLine, MAX_VALUE, fp)) {
    if (strip_comments(sLine, cnf->comment)) { /* 去掉字符串所有空白,注释也忽略 */
      if ('[' == sLine[0] && ']' == sLine[strlen(sLine)-1]) {
        memset(section, '\0', MAX_VALUE); /* 清空section,因为strncpy不追加'\0' */
        strncpy(section, sLine+1, strlen(sLine)-2);
      } else if (NULL != (p = strchr(sLine, cnf->separator))) {  /* 存在分隔符 */
        memset(key,   '\0', MAX_VALUE); /* 清空key,因为strncpy不追加'\0' */
        strncpy(key,  sLine, p - sLine);
        strcpy(value, p + 1); /* strcpy会追加'\0',所以妥妥哒 */
        cnf_add_option(cnf, section, key, value); /* 添加section,key,value */
      } /* 如果该行不存在分隔符则忽略这一行 */
    } /* end strip_comments */
  } /* end while */
  
  fclose(fp);
  return cnf;
}
 
/**
* 获取指定类型的值
* 根据不同类型会赋值给对应值
* 本方法需要注意,int和double的转换,不满足就是0
*     需要自己写代码时判断好
**/
bool cnf_get_value(Config *cnf, const char *section, const char *key)
{
  Data *p = cnf->data; /* 让变量p循环遍历data,找到对应section */
  while (NULL != p && 0 != strcmp(p->section, section)) {
    p = p->next;
  }
  
  if (NULL == p) {
    PRINT_ERRMSG("section not find!");
    return false;
  }
  
  Option *q = p->option;
  while (NULL != q && 0 != strcmp(q->key, key)) {
    q = q->next; /* 遍历option,检查key是否已经存在 */
  }
  
  if (NULL == q) {
    PRINT_ERRMSG("key not find!");
    return false;
  }
  
  strcpy(cnf->re_string, q->value);       /* 将结果字符串赋值 */
  cnf->re_int    = atoi(cnf->re_string);  /* 转换为整形 */
  cnf->re_bool   = 0 == strcmp ("true", cnf->re_string); /* 转换为bool型 */
  cnf->re_double = atof(cnf->re_string);  /* 转换为double型 */
  
  return true;
}
 
/**
* 判断section是否存在
* 不存在返回空指针
* 存在则返回包含那个section的Data指针
**/
Data *cnf_has_section(Config *cnf, const char *section)
{
  Data *p = cnf->data; /* 让变量p循环遍历data,找到对应section */
  while (NULL != p && 0 != strcmp(p->section, section)) {
    p = p->next;
  }
  
  if (NULL == p) { /* 没找到则不存在 */
    return NULL;
  }
  
  return p;
}
 
/**
* 判断指定option是否存在
* 不存在返回空指针
* 存在则返回包含那个section下key的Option指针
**/
Option *cnf_has_option(Config *cnf, const char *section, const char *key)
{
  Data *p = cnf_has_section(cnf, section);
  if (NULL == p) { /* 没找到则不存在 */
    return NULL;
  }
  
  Option *q = p->option;
  while (NULL != q && 0 != strcmp(q->key, key)) {
    q = q->next; /* 遍历option,检查key是否已经存在 */
  }
  if (NULL == q) { /* 没找到则不存在 */
    return NULL;
  }
  
  return q;
}
 
/**
* 将Config对象写入指定文件中
* header表示在文件开头加一句注释
* 写入成功则返回true
**/
bool cnf_write_file(Config *cnf, const char *filename, const char *header)
{
  FILE *fp = fopen(filename, "w");
  if(NULL == fp) {
    PRINT_ERRMSG("fopen");
    exit(errno); /* 读文件错误直接按照错误码退出 */
  }
  
  if (0 < strlen(header)) { /* 文件注释不为空,则写注释到文件 */
    fprintf(fp, "%c %s\n\n", cnf->comment, header);
  }
  
  Option *q;
  Data   *p = cnf->data;
  while (NULL != p) {
    fprintf(fp, "[%s]\n", p->section);
    q = p->option;
    while (NULL != q) {
      fprintf(fp, "%s %c %s\n", q->key, cnf->separator, q->value);
      q = q->next;
    }
    p = p->next;
  }
  
  fclose(fp);
  return true;
}
 
/**
* 删除option
**/
bool cnf_remove_option(Config *cnf, const char *section, const char *key)
{
  Data *ps = cnf_has_section(cnf, section);
  if (NULL == ps) { /* 没找到则不存在 */
    return NULL;
  }
  
  Option *p, *q;
  q = p = ps->option;
  while (NULL != p && 0 != strcmp(p->key, key)) {
    if (p != q) { q = q->next; } /* 始终让q处于p的上一个节点 */
    p = p->next;
  }
  
  if (NULL == p) { /* 没找到则不存在 */
    return NULL;
  }
  
  if (p == q) { /* 第一个option就匹配了 */
    ps->option = p->next;
  } else {
    q->next = p->next;
  }
  
  free(p);
  q = p = NULL; // 避免野指针
  
  return true;
}
 
/**
* 删除section
**/
bool cnf_remove_section(Config *cnf, const char *section)
{
  if (str_empty(section)) {
    return false;
  }
  
  Data *p, *q;
  q = p = cnf->data; /* 让变量p循环遍历data,找到对应section */
  while (NULL != p && 0 != strcmp(p->section, section)) {
    if (p != q) { q = q->next; } /* 始终让q处于p的上一个节点 */
    p = p->next;
  }
  
  if (NULL == p) { /* 没有找到section */
    return false;
  }
  
  if (p == q) { /* 这里表示第一个section,因此链表头位置改变 */
    cnf->data = p->next;
  } else { /* 此时是中间或尾部节点 */
    q->next = p->next;
  }
  
  Option *o = p->option;
  while (NULL != o) {
    free(o); /* 循环释放所有option */
    o = o->next;
  }
  p->option = NULL; // 避免野指针
  free(p); /* 释放删除的section */
  q = p = NULL;  // 避免野指针
  
  return true;
}
 
/**
* 打印当前Config对象
**/
void print_config(Config *cnf)
{
  Data *p = cnf->data; // 循环打印结果
  while (NULL != p) {
    printf("[%s]\n",p->section);
    
    Option *q = p->option;
    while (NULL != q) {
      printf("  %s %c %s\n", q->key, cnf->separator, q->value);
      q = q->next;
    }
    p = p->next;
  }
}
 
/**
* 主程序,放在最底下
* 避免重复声明其他函数
**/
int main(int argc, char *argv[])
{
  // 读取配置文件cnf.ini,注释字符为#,分隔键值字符为=
  Config *cnf = cnf_read_config("cnf.ini", '#', '=');
  if (NULL == cnf) {
    return -1; /* 创建对象失败 */
  }
  
  printf("-------------- After Read File --------------\n");
  print_config(cnf); // 打印cnf对象  
  cnf_remove_section(cnf,"AAA"); // 删除AAA的section
  cnf_remove_option(cnf, "CC","df");  // 删除CC下的df
  printf("-------------- After remove --------------\n");
  print_config(cnf); // 打印cnf对象  
  cnf_add_option(cnf, "NEW", "new_1", "true"); // 新增NEW下的new_1的值
  cnf_add_option(cnf, "NEW", "new_2", "asdas"); // 新增NEW下的new_2的值
  cnf_add_option(cnf, "NEW1", "new_2", "true");
  printf("-------------- After add --------------\n");
  print_config(cnf); // 打印cnf对象  
  
  cnf_get_value(cnf, "NEW1", "new_2"); // 获取NEW1下的new_2值
  printf("cnf_get_value:%s,%d,%d,%f\n",cnf->re_string,cnf->re_int,cnf->re_bool,cnf->re_double);
  
  cnf->separator = ':'; // 将分隔符改成 : ,冒号
  cnf_write_file(cnf, "cnf_new.ini", "write a new ini file!"); // 将对象写入cnf_new.ini文件
  return 0;
}

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lq496387202/article/details/97279675

智能推荐

【C语言初阶】指针-程序员宅基地

文章浏览阅读927次,点赞18次,收藏20次。【C语言初阶】指针变量及其类型的介绍,内存如何编址的介绍,指针+-整数以及指针解引用的用法和意义。

JAVA设计模式之中介者模式详解-程序员宅基地

文章浏览阅读1.6k次,点赞18次,收藏18次。提到中介模式,有一个比较经典的例子就是航空管制。为了让飞机在飞行的时候互不干扰,每架飞机都需要知道其他飞机每时每刻的位置,这就需要时刻跟其他飞机通信。飞机通信形成的通信网络就会无比复杂。这个时候,我们通过引 入“塔台”这样一个中介,让每架飞机只跟塔台来通信,发送自己的位置给塔台,由塔台来 负责每架飞机的航线调度。这样就大大简化了通信网络。_中介者模式

C#中使用System.Runtime.Serialization.Json来进行序列化和反序列化-程序员宅基地

文章浏览阅读7.3k次,点赞2次,收藏11次。基本概念:(1) 序列化就是指把对象转换为字节序列的过程反序列化就是指把字节序列恢复为对象的过程。(2)序列化最重要的作用:在传递和保存对象时.保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。反序列化的最重要的作用:根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。总结:核心作用就是对象状态的保存和重建。(整个过程核心点就是字节流中所保存的对象状态及描述信息)Json(1)什么是JsonJson【javascript对象表示方法】,它是_system.runtime.serialization.json

【小知识】opencv里去掉小连通区域的函数remove_small_objects()解析-程序员宅基地

文章浏览阅读1.1w次,点赞4次,收藏25次。网上搜了好多都没有详细的解释函数里面参数的,参考的python数字图像处理(18):高级形态学处理,码下来省得以后再找了。有些时候,我们只需要一些大块区域,那些零散的、小块的区域,我们就需要删除掉,则可以使用morphology子模块的remove_small_objects()函数。函数格式:skimage.morphology.remove_small_objects(ar, min_..._remove_small_objects

HIVE 字段级血缘分析 写入Neo4j_lineagelogger-程序员宅基地

文章浏览阅读1.1w次,点赞8次,收藏59次。Step 1. 利用HIVE Hook LineageLogger 获取字段之间的依赖关系注意:LineageLogger Hook 是Hive2.0版本之后存在的,如果HIVE版本不够需要升级HIVE版本。PS.CDH有的HIVE版本添加此功能下面以hive 2.5.3版本为例添加参数vim /usr/local/hive/conf/hive-site.xml<property&..._lineagelogger

面试官跟我扯了半小时 CountDownLatch 后,给我发 Offer?| 原力计划-程序员宅基地

文章浏览阅读2.6k次。作者 | 万猫学社责编 | 王晓曼出品 | 程序员宅基地一个长头发、穿着清爽的小姐姐,拿着一个崭新的Mac笔记本向我走来,看着来势汹汹,我心想着肯定是技术大佬吧!但是我也是一个才华横溢的..._cyclbarrycountdownlarch

随便推点

解决-运行程序无法定位程序输入点ucrtbase.abort于动态链接库api-ms-win-crt-runtime-|1-1-0.dll-程序员宅基地

文章浏览阅读7.8w次。这种情况多发生在这样的配置: win7+office2016 + (没装Microsoft Visual C++ 2015 Redistributable)原因:win7下不兼容某些最新软件造成(如office2016)解决办法:或者,安装VC2015最新库补丁。或 更高效:卸载office2016!卸载office2016!卸载office2016!_运行程序无法定位程序输入点ucrtbase.abort于动态链接库api-ms-win-crt-runtime-|

[C++] C++11新特性之智能指针shared_ptr和unique_ptr的使用详解_c++ std::unique_ptr和std::shared_ptr-程序员宅基地

文章浏览阅读1.1k次。参考C++ Primer 第5版C++11 Smart Pointers直接管理动态内存在C++中,动态内存的管理是通过一对运算符来完成的:new:在动态内存中为对象分配空间并返回一个指向该对象的指针,可以选择对对象进行初始化int *pi = new int;//pi指向一个动态分配的、未初始化的无名对象string *ps = new string;//初始化为空stringint *pi = new int(1024);//pi指向的对象的值为1024string *ps =_c++ std::unique_ptr和std::shared_ptr

肥学献礼——自动写诗_自动写诗 链接保留一年-程序员宅基地

文章浏览阅读1.2k次,点赞128次,收藏91次。这里写目录标题导读????第一款效果展示????当然这个也可以用于表白源码????第二款处理结果导读中秋赏月,民间一直流传着多个不同的传说和美丽的神话故事,当然最为人们熟悉的是《嫦娥奔月》了。在多民族的我国,有二十多个民族也在欢庆中秋节,虽然庆祝方式各异,但其寓意,也都是在祁求国泰民安,丰衣足食,团团圆圆,平平安安。这次良辰佳节何不让我们来个吟诗作对岂不美哉!!????第一款特点:根据唐诗数据集设计一款循环神经网络模型,实现你只要输入任意 4 个字和相应的风格,即可以实现一首古诗。赶紧为你的 _自动写诗 链接保留一年

Retrofit2 完全解析 探索与okhttp之间的关系(一)_okhttpclient retrofit2-程序员宅基地

文章浏览阅读933次。一、概述之前写了个okhttputils的工具类,然后有很多同学询问这个工具类和retrofit什么区别,于是上了下官网,发现其底层对网络的访问默认也是基于okhttp,不过retrofit非常适合于restful url格式的请求,更多使用注解的方式提供功能。既然这样,我们本篇博文首先研究其所提供的常用的用法:一般的get请求(如何通过注解携带参数,拼接url)一般的_okhttpclient retrofit2

python中pygame学习——碰碰球(简单版)_pygame的sprite如何画球-程序员宅基地

文章浏览阅读1.1k次。经过三天的学习,算是了解了程序的90%,在这里做个记录。游戏实现:鼠标控制球拍左右移动,球每次碰到顶部得一分,碰左右反弹,一共3条命,有音效。附件内容:游戏代码,图片及声音文件import pygameimport sys#利用动画精灵创建球类并定义move方法class MyBallCalss(pygame.sprite.Sprite): def __init__..._pygame的sprite如何画球

Tensorflow error: Could not find a version that satisfies the requirement tensorflow-程序员宅基地

文章浏览阅读1.2w次,点赞11次,收藏8次。出现这个问题是由于下面两种情况其中得一项造成的,大家可以逐个排查,试一下1,版本兼容问题截止到 2020.10左右,Tensorflow 社区可支持的 Python 版本从 3.6-3.8,且在 windows 中Python 需是 64位(Tensorflow 暂不支持 32 位)查看自己 Python 版本及位数方法,打开命令行,直接在命令行中输入 python 命令即可,如下:版本不符或位数不符的,需要重新下载关于 Tensorflow 兼容 python 版本具体细节,可参考官方手册h_error: could not find a version that satisfies the requirement tensorflow (f