INI文件解析C函数库-iniparser
关于INI文件INI(Initialization File)文件是一种简单直观的数据存储格式,常用于配置应用程序的初始化设置。这种文件通常包含若干个节(section)和键值对(key-value pairs)。INI文件的每一部分都是自描述性的,易于阅读和编辑,使得非程序员也能轻易理解并修改配置参数。
INI文件因其简单易用性而在许多编程语言中广泛应用,尤其是在Windows操作系统中,很多应用程序都采用INI文件作为配置文件。当然,随着XML、JSON等更丰富、更结构化的数据交换格式的普及,INI文件在现代应用程序中的使用相对减少,但在一些轻量级应用或对启动速度有较高要求的情况下,仍然是一种常见且实用的配置文件格式。
文件结构
节(Section):
INI文件中的各个部分通过方括号 [] 包裹的名称来定义,例如 **[Section1]**。每个节可以包含多个键值对。
键值对(Key-Value Pairs):
键和值之间用等号 = 分隔,如 key1=value1。键通常是描述性质的字符串,而值则可以是字符串、数字或其他类型的数据。
注释:
注释行以分号 ; 开始,直到行尾都被视为注释内容,不会被程序解析。
多行值:
某些INI解析器允许值跨越多行,通常通过在行尾添加反斜杠 \ 来延续到下一行
文件举例
;author yikoupeng
[BASIC_INFO]
version = V1.1.1.1
user = yikou
number = 999
[FTP]
ftppath = /home/ftp
ftpuser = ftp
ftppass = 123456
port = 21
......

图源“一口Linux”,感谢原作者,其中:
注释以分号(;)开头
[BASIC_INFO]、[FTP]就是组名,
组成员有“version”........“ftppath”...
注意:
每个组下的key是唯一不能重复的,但不同组下可以存在相同的key。
软件使用过程中,配置文件的处理常常是一个重要的环节。iniparser 作为一个用于解析.ini 格式文件的C语言工具库,为开发者提供了便捷高效的解决方案。采用 MIT 许可协议授权。
iniParser is a simple C library offering ini file parsing services. The library is pretty small (less than 1500 lines of C) and robust, and does not depend on any other external library to compile. It is written in ANSI C and should compile on most platforms without difficulty.
iniparser 作为一个用于解析 INI 文件的强大库,常用于 C 语言项目中。其优势在于其强大的功能和灵活性,它能够准确地解析 INI 文件中的各个节(section)和键值对,将配置信息以易于使用的方式呈现给开发者。无论是简单的配置文件还是复杂的多层结构,iniparser 都能轻松应对。iniparser做为一个C语言库,用于解析和操作 INI 格式的配置文件,是针对INI文件的开源解析器;它可以对配置文件进行解析、添加、修改、删除等操作。
Key Features
Small : around 1500 sloc inside 4 files (2 .c and 2 .h)
Portable : no dependancies, written in -pedantic C
Fully re-entrant : easy to make it thread-safe (just surround library calls by mutex)
例如,在应用程序的配置管理中,iniparser 可以帮助开发者快速读取和修改配置参数,实现应用程序的灵活定制。在服务器配置中,它可以解析各种服务器参数,确保服务器的正确运行。
安装 iniparser
在使用之前需要确保已经安装了 iniparser。可以从其 GitHub 仓库下载并编译:
git clone https://github.com/ndevilla/iniparser.git
cd iniparser
make && make install
API(应用编程程序接口)
dictionary.h里面声明了一些直接解析ini file的API,iniparser.h头文件里面声明了一些提供用户操作的API。
iniparser.h里面的API是对dictionary.h里面API的再次封装,以提供用户友好性。该头文件里面的主要API如下:
1.加载ini文件
/*
* @brief 从ini格式的配置文件中加载数据
* @param [IN] ininame 要打开的ini格式文件
* @return != NULL 返回一个指向dictionary结构的指针
* == NULL 加载ini文件失败
*/
dictionary * iniparser_load(const char *ininame);
2.获取键值
/*
* @brief 获取指定键(key)对应的字符串类型的值
* @param [IN] d dictionary结构的指针
* @param [IN] key 要查找的键,通常格式为 "section:key",表示要获取哪个节(section)下的哪一项(key)的值。
* @param [IN] def 当键不存在或者其值不是字符串时的默认返回值。如果没有找到对应键,函数将返回此默认值。
* @return 如果找到了相应的键,返回键值对应字符串
* 如果没有找到匹配的键,返回def指定的字符串值
*/
const char * iniparser_getstring(const dictionary *d, const char *key, const char *def);
/*
* @brief 获取指定键(key)对应的整数值
* @param [IN] d dictionary结构的指针
* @param [IN] key 要查找的键,通常格式为 "section:key",表示要获取哪个节(section)下的哪一项(key)的值。
* @param [IN] notfound 当键不存在或者其值不能被转换为整数时,函数将返回这个默认值。
* @return 如果找到了相应的键,并且其值可以被成功转换为整数,则返回该整数值。
* 如果没有找到匹配的键,或者该键对应的值无法转换为整数,则返回 notfound 参数提供的默认值。
*/
int iniparser_getint(const dictionary * d, const char * key, int notfound);
/*
* @brief 获取指定键(key)对应的浮点型值
* @param [IN] d dictionary结构的指针
* @param [IN] key 要查找的键,通常格式为 "section:key",表示要获取哪个节(section)下的哪一项(key)的值。
* @param [IN] notfound 当键不存在或者其值无法转换为双精度浮点数时,函数返回的默认值。
* @return 如果找到了相应的键,并且其值能成功转换为一个双精度浮点数,则返回该浮点数。
* 如果没有找到匹配的键,或者键的值不能被解释为一个有效的双精度浮点数,则返回 notfound 参数所提供的默认值。
*/
double iniparser_getdouble(const dictionary *d, const char *key, double notfound);
3.设置键值
/*
* @brief 设置或修改 ini 配置文件中某个键值对
* @param [IN] d dictionary结构的指针
* @param [IN] entry 字符串形式的键值对标识符,格式通常是 "section:key",表明您要在哪个节(section)下的哪个键(key)上设置或修改值(val)。
* key值存在则修改对应val,key值不存在则会新增
* @param [IN] val: 要设置的新值,作为字符串传递。
* @return 返回0表示设置成功
*/
int iniparser_set(dictionary *ini, const char *entry, const char *val);
4.移除键值
/*
* @brief 移除 ini 配置文件中某个键值对
* @param [IN] d dictionary结构的指针
* @param [IN] entry 字符串形式的键名,包括可选的部分名称(section)和键(key)
* 如果不指定key,则会移除整个section
*/
void iniparser_unset(ini, const char *entry);
5.判断键是否存在
/*
* @brief 判断 ini 配置文件是否存在某个键值
* @param [IN] d dictionary结构的指针
* @param [IN] entry 字符串形式的键值对标识符,格式通常是 "section:key"
* @return 返回1表示存在,返回0表示不存在
*/
int iniparser_find_entry(const dictionary *ini, const char *entry);
6.获取section个数
/*
* @brief 获取ini配置文件中section的数量
* @param [IN] d dictionary结构的指针
* @return 成功返回section个数,失败返回 -1
*/
int iniparser_getnsec(const dictionary * d);
/*
* @brief 获取某个section值
* @param [IN] d dictionary结构的指针
* @param [IN] n 指定获取第几个section值
* @return 成功返回获取到的section值,失败返回NULL
*/
const char *iniparser_getsecname(const dictionary * d, int n);
7.获取section下key个数
/*
* @brief 获取ini配置文件中某个section的key个数
* @param [IN] d dictionary结构的指针
* @param [IN] s section
* @return 返回指定section下的key个数
*/
int iniparser_getsecnkeys(dictionary * d, char * s);
/*
* @brief 获取ini配置文件中某个section的所有key
* @param [IN] d dictionary结构的指针
* @param [IN] s section
* @param [OUT] keys 通过这个参数输出key,也可以通过返回值获取
* @return 成功返回指定section下的key,失败返回NULL
*/
const char **iniparser_getseckeys(const dictionary *d, const char *s, const char **keys)
8.保存dictionary对象到文件中
/*
* @brief 保存dictionary对象到文件中
* @param [IN] d dictionary结构的指针
* @param [IN] f 已打开的文件描述符
*/
void iniparser_dump_ini(const dictionary *d, FILE *f);
9.释放dictionary对象
/*
* @brief 释放dictionary对象
* @param [IN] d dictionary结构的指针
*/
void iniparser_freedict(dictionary * d);
10.api汇总
iniparser.h头文件里面的API
//获取dictionary对象的section个数
int iniparser_getnsec(dictionary * d);
//获取dictionary对象的第n个section的名字
char * iniparser_getsecname(dictionary * d, int n);
//保存dictionary对象到file
void iniparser_dump_ini(dictionary * d, FILE * f);
//保存dictionary对象一个section到file
void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f);
//保存dictionary对象到file
void iniparser_dump(dictionary * d, FILE * f);
//获取dictionary对象某个section下的key个数
int iniparser_getsecnkeys(dictionary * d, char * s);
//获取dictionary对象某个section下所有的key
char ** iniparser_getseckeys(dictionary * d, char * s);
//返回dictionary对象的section:key对应的字串值
char * iniparser_getstring(dictionary * d, const char * key, char * def);
//返回idictionary对象的section:key对应的整形值
int iniparser_getint(dictionary * d, const char * key, int notfound);
//返回dictionary对象的section:key对应的双浮点值
double iniparser_getdouble(dictionary * d, const char * key, double notfound);
//返回dictionary对象的section:key对应的布尔值
int iniparser_getboolean(dictionary * d, const char * key, int notfound);
//设置dictionary对象的某个section:key的值
int iniparser_set(dictionary * ini, const char * entry, const char * val);
//删除dictionary对象中某个section:key
void iniparser_unset(dictionary * ini, const char * entry);
//判断dictionary对象中是否存在某个section:key
int iniparser_find_entry(dictionary * ini, const char * entry) ;
//解析dictionary对象并返回(分配内存)dictionary对象
dictionary * iniparser_load(const char * ininame);
//释放dictionary对象(内存)
void iniparser_freedict(dictionary * d);
dictionary.h头文件里面的API
//计算关键词的hash值
unsigned dictionary_hash(const char * key);
//创建dictionary对象
dictionary * dictionary_new(int size);
//删除dictionary对象
void dictionary_del(dictionary * vd);
//获取dictionary对象的key值
char * dictionary_get(dictionary * d, const char * key, char * def);
//设置dictionary对象的key值
int dictionary_set(dictionary * vd, const char * key, const char * val);
//删除dictionary对象的key值
void dictionary_unset(dictionary * d, const char * key);
//保存dictionary对象
void dictionary_dump(dictionary * d, FILE * out);
示例代码1
一个简单的示例,演示如何使用 iniparser 读取和写入 INI 文件。以下是一个简单的代码示例,展示如何使用 iniparser 来读取和写入 INI 文件。
1. 读取 INI 文件
#include <stdio.h>
#include <iniparser.h>
int main() {
// 读取配置文件
dictionary *ini = iniparser_load("config.ini");
if (ini == NULL) {
printf("Cannot load 'config.ini'\n");
return 1;
}
// 获取值
const char *host = iniparser_getstring(ini, "database:host", NULL);
int port = iniparser_getint(ini, "database:port", -1);
// 打印值
printf("Host: %s\n", host);
printf("Port: %d\n", port);
// 释放字典
iniparser_freedict(ini);
return 0;
}
2. 写入 INI 文件
#include <stdio.h>
#include <iniparser.h>
int main() {
// 创建配置文件
dictionary *ini = iniparser_new(0);
// 设置值
iniparser_set(ini, "database:host", "localhost");
iniparser_set(ini, "database:port", "5432");
// 保存到文件
iniparser_dump_ini(ini, fopen("config.ini", "w"));
// 释放字典
iniparser_freedict(ini);
return 0;
}
config.ini 示例
为了配合上述代码,可以创建一个 config.ini 文件,内容如下:
[database]
host=localhost
port=5432
编译和运行
可以使用以下命令编译你的 C 文件:
gcc -o freeoa freeoa.c -liniparser
然后运行生成的可执行文件:
./freeoa
这个示例展示了如何使用 iniparser 读取和写入 INI 文件。
为了满足更复杂的项目需求,可以适当增加代码来扩展 iniparser 的功能。比如,可以添加对特定数据类型的自动转换功能,或者实现更高级的配置文件验证逻辑;还可以结合其他库和工具,进一步增强 iniparser 在特定应用场景下的实用性。
在实际应用中,iniparser 已经被广泛应用于各种软件项目中。它的可靠性和高效性使得开发者能够专注于业务逻辑的实现,而不必在配置文件处理上花费过多精力。
示例代码2
1、config.ini
编写配置文件:
config.ini
[BASIC_INFO]
version = V1.1.1.1
user = yikou
number = 999
[FTP]
ftppath = /home/ftp
ftpuser = ftp
ftppass = 123456
port = 21
[NETWORK]
interface = eth1
dns1 = 8.8.8.8
dns2 = 8.8.8.8
subnet = 255.255.255.0
router = 192.168.3.1
2、读取配置参数
尝试编写iniparser程序对ini文件进行修改:
#include <stdio.h>
#include "iniparser.h"
#include "dictionary.h"
#define PATH "config.ini"
typedef unsigned char BYTE;
typedef unsigned char UINT8;
typedef unsigned char UCHAR;
typedef unsigned short int UINT16;
typedef unsigned long int UINT32;
struct device_cfg_s{
/*basicinfo*/
char version[32];
char user[32];
int number;
/*ftp*/
char ftppath[128];
char ftpuser[32];
char ftppass[32];
UINT16 port;
/*network*/
char interface[16];
char dns1[32];
char dns2[32];
char subnet[32];
char router[32];
};
struct device_cfg_s devcfg;
int cfg_load(char *name) {
dictionary *ini= NULL;
/* 解析dictionary对象并返回(分配内存)dictionary对象*/
ini = iniparser_load(name);
if( ini ==NULL) {
printf("iniparser failure\n");
return -1;
}
/*basicinfo*/
strcpy(devcfg.version,iniparser_getstring(ini, "BASIC_INFO:version", "v0.0.0.0"));
strcpy(devcfg.user ,iniparser_getstring(ini, "BASIC_INFO:user", "yikou"));
devcfg.number = iniparser_getint(ini, "BASIC_INFO:number", 666);
/*ftp*/
strcpy(devcfg.ftppath ,iniparser_getstring(ini, "FTP:ftppath", "/"));
strcpy(devcfg.ftpuser ,iniparser_getstring(ini, "FTP:ftpuser", "ftp"));
strcpy(devcfg.ftppass ,iniparser_getstring(ini, "FTP:ftppass", "123456"));
devcfg.port = iniparser_getint(ini, "FTP:port", 21);
/*network*/
strcpy(devcfg.interface,iniparser_getstring(ini, "NETWORK:interface", "eth0"));
strcpy(devcfg.dns1,iniparser_getstring(ini, "NETWORK:dns1", NULL));
strcpy(devcfg.dns2 ,iniparser_getstring(ini, "NETWORK:dns2", NULL));
strcpy(devcfg.subnet ,iniparser_getstring(ini, "NETWORK:subnet", "255.255.255.0"));
strcpy(devcfg.router ,iniparser_getstring(ini, "NETWORK:router", "192.168.3.1"));
/* 返回dictionary对象的section,key对应的字串值 */
printf("version:%s\n",devcfg.version);
printf("user:%s\n", devcfg.user);
printf("number:%d\n",devcfg.number);
printf("ftppath:%s\n", devcfg.ftppath);
printf("ftpuser:%s\n",devcfg.ftpuser);
printf("ftppass:%s\n", devcfg.ftppass);
printf("port:%d\n",devcfg.port);
printf("interface:%s\n", devcfg.interface);
printf("dns1:%s\n",devcfg.dns1);
printf("dns2:%s\n", devcfg.dns2);
printf("subnet:%s\n",devcfg.subnet);
printf("router:%s\n", devcfg.router);
iniparser_freedict(ini);
}
int main (int argc, char **argv) {
cfg_load(PATH);
//cfg_save_key(PATH,"BASIC_INFO","chnAddr","3501");
//cfg_save_key(PATH,"BASIC_INFO","enddeviceNo","87564289");
return 0;
}
可以看到最终值以配置文件中的为准。
如果配置文件没有配置参数则以iniparser_getxxxxx()中默认值为准。
3、保存配置信息到文件
为方便保存键值封装了函数
int cfg_save_key(char *filename,char *section,char *key,char *value)
参数:
filename 配置文件名
section 节名字
key 键
value 值
int cfg_save_key(char *filename,char *section,char *key,char *value) {
FILE *fp = NULL ;
dictionary *ini= NULL;
char item[128]={0};
/* 解析dictionary对象并返回(分配内存)dictionary对象*/
ini = iniparser_load(filename);
if( ini ==NULL){
printf("iniparser failure\n");
return -1;
}
sprintf(item,"%s:%s",section,key);
/* 设置dictionary对象的某个section:key的值 */
iniparser_set(ini, item, value);
fp = fopen(filename, "w");
if( fp == NULL ) {
printf("stone:fopen error!\n");
exit(-1);
}
/* 保存dictionary对象 */
// iniparser_dumpsection_ini(ini, "BASIC_INFO", fp);
iniparser_dump_ini(ini, fp);
fclose(fp);
/* 释放dictionary对象(内存)*/
iniparser_freedict(ini);
}
例如修改BASIC_INFO节的user的值为yikoupeng,number值为12345
cfg_save_key(PATH,"BASIC_INFO","user","yikoupeng");
cfg_save_key(PATH,"BASIC_INFO","number","12345");
执行结果可以看到配置文件basic_info节的user 和number 键值对被修改。
最新版本:4.2
项目主页:https://github.com/ndevilla/iniparser