在C语言开发中,经常需要处理INI配置文件。而inih,这个仅用几页代码写成的轻量级INI文件解析器,凭借其简洁高效的设计,成为了嵌入式系统和资源受限环境下的首选方案。接下来介绍inih的特点,并提供一个简单示例。
源码地址:https://github.com/benhoyt/inih/tags
inih (INI Not Invented Here) 是一个用C语言编写的INI文件解析器,它秉承“简单就是美”的设计理念,代码精简,易于理解和维护。 它不仅体积小巧,适合在嵌入式系统等资源受限的环境中使用,而且功能强大,支持RFC 822风格的多行条目和名称:值对,与Python的ConfigParser模块兼容性良好。
inih采用SAX(Simple API for XML)风格的解析方式,它不会一次性将整个INI文件加载到内存中,而是逐行读取并处理。这使得它能够高效地处理大型INI文件,即使在内存紧张的情况下也能保持稳定运行。 这种方式也使得inih的实现更加简洁,易于理解和调试。
inih 提供了多种接口函数,以适应不同的输入方式:
• ini_parse(file_path, handler, user)
: 从指定文件路径解析INI文件。
• ini_parse_file(file, handler, user)
: 从FILE*对象解析INI文件。
• ini_parse_string(string, handler, user)
: 从字符串解析INI文件。
• ini_parse_stream(reader, handler, user)
: 使用自定义的fgets风格读取函数解析INI文件,这为处理各种自定义IO流提供了极大的灵活性。
所有这些函数都使用一个回调函数handler
来处理解析结果,回调函数会接收到section、name和value三个参数,方便开发者进行自定义处理
inih 提供了丰富的编译选项,允许开发者根据自己的需求定制解析器的行为:
• 语法选项: 你可以控制是否允许多行条目、UTF-8 BOM、行内注释、行首注释以及是否允许没有值的名称。
• 解析选项: 你可以选择在遇到第一个错误时停止解析,或者在回调函数中获取行号信息,以及在遇到新的section时是否调用handler函数。
• 内存选项: 你可以选择在栈上还是在堆上分配内存,以及设置最大行长和初始内存分配大小。你甚至可以自定义内存分配器函数。
以下是一个简单的C语言示例,展示了如何使用inih解析INI文件:
#include
#include
#include
#include "ini.h"
/*
[protocol]
version=6
[user]
name=alex
age=12
*/
#define INI_PATH "test.ini"
typedef struct _configure{
char name[64];
int age;
int version;
}Configure;
int test_ini_handler(void* user, const char* section, const char* name, const char* value)
{
Configure* config = (Configure*)user;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if (MATCH("user", "name")) {
strcpy_s(config->name, value);
}
else if (MATCH("user", "age")) {
config->age = atoi(value);
}
else if (MATCH("protocol", "version")) {
config->version = atoi(value);
}
else {
return -1;//未知字段
}
return 0;
}
int main()
{
int ret = 0;
Configure config;
ret = ini_parse(INI_PATH, test_ini_handler, &config);
if (ret < 0) {
printf("load ini error\n");
return -1;
}
printf("name:%s age:%d version:%d\n", config.name, config.age, config.version);
return 0;
}
inih 还提供了一个简单的C++包装类INIReader
,使得在C++环境下使用inih更加方便。 它使用STL的map存储解析结果,并提供了一系列getter函数来访问配置值。
inih与Python的ConfigParser模块在一些细节上存在差异,例如处理没有section的name=value对和多行条目的方式。 开发者在使用inih时需要了解这些差异,并根据实际情况进行调整。
平台注意事项:
在Windows平台下,为了处理Unicode路径,需要使用_wfopen()
函数打开文件。
#include
#include
#include
#include "INIReader.h"
using namespace std;
/*
[protocol]
version=6
[user]
name = Bob Smith
email = [email protected]
active = true
pi = 3.14159
*/
#define INI_PATH "test.ini"
int main()
{
INIReader reader(INI_PATH);
if (reader.ParseError() < 0) {
printf("load ini file failed\n");
return -1;
}
if (reader.HasSection("protocol")) {
printf("section protocol exists\n");
}
if (reader.HasValue("user", "pi")) {
printf("section user ;value pi exists\n");
}
int protocol_version = reader.GetInteger("protocol", "version", 0);
printf("protocol_version = %d\n", protocol_version);
string user_name = reader.GetString("user", "name", "UNKNOWN");
printf("user_name:%s\n", user_name.c_str());
string user_email = reader.GetString("user", "email", "UNKNOWN");
printf("user_email:%s\n", user_email.c_str());
bool user_active = reader.GetBoolean("user", "active", false);
if (user_active) {
printf("user active is true\n");
}
double user_pi = reader.GetReal("user", "pi", 0.0);
printf("user_pi:%f\n", user_pi);
return 0;
}