原地址: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 || == 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 && != strcmp(p->section, section)) {
p = p->next;
} if (NULL == p) { /* 说明没有找到section,需要加一个 */
Data *ps = (Data*)malloc(sizeof(Data));
if (NULL == ps) {
exit(-); /* 申请内存错误 */
}
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 && != strcmp(q->key, key)) {
q = q->next; /* 遍历option,检查key是否已经存在 */
} if (NULL == q) { /* 不存在option,则新建一个 */
q = (Option*)malloc(sizeof(Option));
if (NULL == q) {
exit(-); /* 申请内存错误 */
}
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 ( == isspace(*p)) {
*q++ = *p; /* 不是空白字符则重写字符串 */
}
}
*q = '\0'; return != 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[] && ']' == sLine[strlen(sLine)-]) {
memset(section, '\0', MAX_VALUE); /* 清空section,因为strncpy不追加'\0' */
strncpy(section, sLine+, strlen(sLine)-);
} else if (NULL != (p = strchr(sLine, cnf->separator))) { /* 存在分隔符 */
memset(key, '\0', MAX_VALUE); /* 清空key,因为strncpy不追加'\0' */
strncpy(key, sLine, p - sLine);
strcpy(value, p + ); /* 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 && != strcmp(p->section, section)) {
p = p->next;
} if (NULL == p) {
PRINT_ERRMSG("section not find!");
return false;
} Option *q = p->option;
while (NULL != q && != 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 = == 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 && != 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 && != 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 ( < 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 && != 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 && != 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 -; /* 创建对象失败 */
} 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 ;
}

C语言实现<读取>和<写入> *.ini文件(转)的更多相关文章

  1. 用C#读取,写入ini文件

    [DllImport("kernel32.dll")] private static extern bool WritePrivateProfileString(string se ...

  2. 【WPS】表格使用VBA宏编程写入ini文件实现软件多语言

    前言:公司软件最近在做多语言版本,而又来一个西班牙文版本的,之前已经做过中文版本,英文版本和法文版本,之前是同事做的,现在安排我做,之前的做法,使用wps表格,翻译好,然后一个一个复制粘贴到ini文件 ...

  3. MFC写入.ini文件的策略

    在使用WritePrivateProfileString的时候, 如果前两个参数已经在INI文件中存在,那该函数的作用就是修改键值,即楼主说的覆盖 如果第一个参数存在,第二个参数不存在,那作用是在IN ...

  4. python读取并写入mat文件

    用matlab生成一个示例mat文件: clear;clc matrix1 = magic(5); matrix2 = magic(6); save matData.mat 用python3读取并写入 ...

  5. Asp.net读取和写入txt文件方法(实例)!

    Asp.NET读取和写入txt文件方法(实例)! [程序第一行的引入命名空间文件 - 参考] System; using System.Collections; using System.Config ...

  6. C语言实现 读取写入ini文件实现(转)

    #include <stdio.h> #include <string.h> /* * 函数名: GetIniKeyString * 入口参数: title * 配置文件中一组 ...

  7. C#读取和写入XML文件

    关于xml是属于一个比较重要的东西,在平时开发的过程中,这块内容最主要的是要掌握XML内容的读取和写入操作. 一.什么是XML? XML 指可扩展标记语言(EXtensible Markup Lang ...

  8. java读取数据写入txt文件并将读取txt文件写入另外一个表

    package com.xsw.test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.F ...

  9. Java——读取和写入txt文件

    package com.java.test.a; import java.io.BufferedReader; import java.io.BufferedWriter; import java.i ...

随机推荐

  1. Eclipse修改已存在的SVN地址

    1.Window---->Show View---->Other...

  2. spark RDD底层原理

    RDD底层实现原理 RDD是一个分布式数据集,顾名思义,其数据应该分部存储于多台机器上.事实上,每个RDD的数据都以Block的形式存储于多台机器上,下图是Spark的RDD存储架构图,其中每个Exe ...

  3. 小程序配置单个页面导航栏的属性(微信小程序交流群:604788754)

    配置单个页面导航栏的属性: 就在所要配置页面相对应的json文件中写入以下想要设置的属性: { "navigationBarBackgroundColor": "#fff ...

  4. 使用shake.js让你博客支持摇一摇

    大家好,又到了随机文章的时间,请使用手机打开演示站点,然后像摇妹子一样摇晃手机,你会发现非常牛逼的事情,炫酷吧.该功能已经集成在Oconnor1.8中.本文主要讲解这货的原理. 首先需要下载shake ...

  5. 做了一个vue的同步tree 的npm组件包

    前言:因为现成的tree组件没有找到.亦或是其依赖的其他东西太多,不太合适引入我们的项目,所以自己做了一个.大概样式: 在线例子: https://hamupp.github.io/t-vue-tre ...

  6. java学习笔记 --- 集合(续)

    1.map集合 1.1.特点:将键映射到值的对象.一个映射不能包含重复的键:每个键最多只能映射到一个值. 1.2.Map集合和Collection集合的区别? Map集合存储元素是成对出现的,Map集 ...

  7. AVG Internet Security 2013 – 免费1年

    AVG 是一款来自捷克的老牌的且非常优秀的免费杀毒软件,自推出以来就颇受用户好评!一直以来诸多的优点使其获得不少忠实用户,即便在中国也赢得了不少粉丝!AVG的安全产品广泛地被欧美以及大洋洲地区使用,并 ...

  8. if...then

    我到现在明白一些道理 有些事情是徒劳无功的 有些却不是 世间事不过如此,只要你肯付出相应的代价,你就可以得到.

  9. js 常用事件

    onclick 事件会在对象被点击时发生. 请注意, onclick 与 onmousedown 不同.单击事件是在同一元素上发生了鼠标按下事件之后又发生了鼠标放开事件时才发生的. 如:点击验证码时进 ...

  10. Bugzilla使用规范

    登陆Bugzilla Bugzilla登陆地址: http://172.21.8.39:21500/manual/ 账号:XXX@sim.com 密码:123456 Bugzilla简介 Bugzil ...