inih轻量级的ini文件解析库

在C语言开发中,经常需要处理INI配置文件。而inih,这个仅用几页代码写成的轻量级INI文件解析器,凭借其简洁高效的设计,成为了嵌入式系统和资源受限环境下的首选方案。接下来介绍inih的特点,并提供一个简单示例。

源码地址:https://github.com/benhoyt/inih/tags

inih:小巧而强大的INI解析器

inih (INI Not Invented Here) 是一个用C语言编写的INI文件解析器,它秉承“简单就是美”的设计理念,代码精简,易于理解和维护。 它不仅体积小巧,适合在嵌入式系统等资源受限的环境中使用,而且功能强大,支持RFC 822风格的多行条目和名称:值对,与Python的ConfigParser模块兼容性良好。

SAX风格解析:高效且节省内存

inih采用SAX(Simple API for XML)风格的解析方式,它不会一次性将整个INI文件加载到内存中,而是逐行读取并处理。这使得它能够高效地处理大型INI文件,即使在内存紧张的情况下也能保持稳定运行。 这种方式也使得inih的实现更加简洁,易于理解和调试。

灵活的接口:适应多种IO方式

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

inih 提供了丰富的编译选项,允许开发者根据自己的需求定制解析器的行为:

  • • 语法选项: 你可以控制是否允许多行条目、UTF-8 BOM、行内注释、行首注释以及是否允许没有值的名称。

  • • 解析选项: 你可以选择在遇到第一个错误时停止解析,或者在回调函数中获取行号信息,以及在遇到新的section时是否调用handler函数。

  • • 内存选项: 你可以选择在栈上还是在堆上分配内存,以及设置最大行长和初始内存分配大小。你甚至可以自定义内存分配器函数。

简单易用的C语言示例

以下是一个简单的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;
}

更便捷的C++接口:INIReader

inih 还提供了一个简单的C++包装类INIReader,使得在C++环境下使用inih更加方便。 它使用STL的map存储解析结果,并提供了一系列getter函数来访问配置值。

与ConfigParser的差异:

inih与Python的ConfigParser模块在一些细节上存在差异,例如处理没有section的name=value对和多行条目的方式。 开发者在使用inih时需要了解这些差异,并根据实际情况进行调整。

平台注意事项:

在Windows平台下,为了处理Unicode路径,需要使用_wfopen()函数打开文件。

C++代码示例

#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;
}

你可能感兴趣的:(inih)