C++实用工具类-ini配置文件解析

dcc084fe-a937-4cf6-9b2d-f59765a1db19

ini配置文件介绍

一种常见的kv结构的配置文件

主要格式为 key=value

同时支持section, kv归属于一个section 如:

[default]

key1=value1

key2=value2

[section2]

key1=value1

key2=value2

注释, 支持#作为注释开始标记, 本实现仅支持 #单独一行的情况

实现方式

解析文件后按照状态流转进行读取, 如下:

C++实用工具类-ini配置文件解析

全部读取文件内容到内存后, 按照状态流转一次循环全部字符完成解析,解析内容存放成2级map结构

section-->IniSection

IniSection内部存放 key->value map

并对外提供查询value接口

 实现代码

代码目录:

C++实用工具类-ini配置文件解析

IniConfig.h

#ifndef __INICONFIG_H__
#define __INICONFIG_H__

#include <string>
#include <map>
#include "IniSection.h"

class IniConfig
{
public:
    IniConfig();

    ~IniConfig();

    bool LoadFile(const std::string& iniFilePath);

    std::string GetValue(const std::string& secName, const std::string& key);

    int GetIntValue(const std::string& secName, const std::string& key);

private:
    std::map<std::string, IniSection> configMap_;
};

#endif

IniConfig.cpp

#include "IniConfig.h"
#include <fstream>
#include <string.h>
#include <memory>
using namespace std;

IniConfig::IniConfig()
{
}

IniConfig::~IniConfig()
{
}

enum class IniStat : int
{
    Start, //开始
    Section, //读取section部分
    Key, //读取key
    Value, //读取value
    Comment //注释
};

bool IniConfig::LoadFile(const string& iniFilePath)
{
    fstream fs;
    fs.open(iniFilePath, ios::in);
    if(!fs.is_open())
    {
        return false;
    }

    fs.seekg(0, ios::end);
    int length = fs.tellg();
    fs.seekg(0, ios::beg);

    char* buffer = new char[length+1];
    std::unique_ptr<char[]> bufferPtr(buffer);
    memset(buffer, 0, length+1);
    fs.read(buffer, length);
    
    IniStat stat = IniStat::Start;
    int pos = 0;
    string key;
    string value;
    IniSection iniSection;
    while(pos < length)
    {
        if(buffer[pos] == '[')
        {
            stat = IniStat::Section;
            configMap_.insert(make_pair(iniSection.sectionName_, iniSection));
            iniSection.Clear();
        }
        else if(stat == IniStat::Section)
        {
            if(buffer[pos] == ']')
            {
                stat = IniStat::Key;
            }
            else
            {
                iniSection.sectionName_ += buffer[pos];
            }
        }
        else if(buffer[pos] == '=')
        {
            stat = IniStat::Value;
        }
        else if(buffer[pos] == 'r' || buffer[pos] == 'n')
        {
            if(!key.empty())
            {
                iniSection.InsertItem(key, value);
                key.clear();
                value.clear();
            }
            stat = IniStat::Key;
        }
        else if(buffer[pos] == '#')
        {
            stat = IniStat::Comment;
        }
        else if(stat == IniStat::Key)
        {
            key += buffer[pos];
        }
        else if(stat == IniStat::Value)
        {
            value += buffer[pos];
        }
        pos++;
    }

    //最后没有换行的情况
    if(!key.empty())
    {
        iniSection.InsertItem(key, value);
    }

    if(!iniSection.Empty())
    {
        configMap_.insert(make_pair(iniSection.sectionName_, iniSection));
    }

    return true;
}

std::string IniConfig::GetValue(const std::string& secName, const std::string& key)
{
    auto itSection = configMap_.find(secName);
    if(itSection != configMap_.end())
    {
        return itSection->second.GetValue(key);
    }
    return "";
}

int IniConfig::GetIntValue(const std::string& secName, const std::string& key)
{
    string value = GetValue(secName, key);
    return atoi(value.c_str());
}

IniSection.h

#ifndef  __INISECTION_H__
#define __INISECTION_H__

#include <string>
#include <map>

class IniSection
{
public:
    IniSection();

    ~IniSection();

    bool Empty() const;

    void Clear();

    void InsertItem(const std::string& key, const std::string& value);

    std::string GetValue(const std::string& key);

    std::string sectionName_;
private:
    std::map<std::string, std::string> itemMap_;

};

#endif

IniSection.cpp

#include "IniSection.h"

IniSection::IniSection()
{  
}

IniSection::~IniSection()
{
}

void IniSection::Clear()
{
    sectionName_.clear();
    itemMap_.clear();
}

bool IniSection::Empty() const
{
    return sectionName_.empty();
}

void IniSection::InsertItem(const std::string& key, const std::string& value)
{
    itemMap_.insert(make_pair(key, value));
}

std::string IniSection::GetValue(const std::string& key)
{
    auto it = itemMap_.find(key);
    if(it != itemMap_.end())
    {
        return it->second;
    }
    return "";
}

测试代码:

#include "gtest.h"
#include "IniConfig.h"
#include <vector>
using namespace std;

class IniConfigTest : public ::testing::Test
{
};

TEST_F(IniConfigTest, TestSimple)
{
    IniConfig iniConfig;
    if(!iniConfig.LoadFile("./src/unittest/test.ini"))
    {
        GTEST_FAIL();
        return;
    }

    EXPECT_TRUE(iniConfig.GetValue("default","app_name") == "IniConfigTest");
    cout << "default.app_name=" << iniConfig.GetValue("default","app_name") << endl;

    EXPECT_TRUE(iniConfig.GetValue("default","testkey3") == "testvalue3");
    cout << "default.testkey3=" << iniConfig.GetValue("default","testkey3") << endl;
    
    EXPECT_TRUE(iniConfig.GetIntValue("default","testkey4") == 200);

    string section1_app_name = iniConfig.GetValue("section1","app_name");
    cout << "section1.app_name=" << section1_app_name << endl;
    EXPECT_TRUE(section1_app_name == "section1_IniConfigTest");
}

测试用ini文件

[default]
#test comment
app_name=IniConfigTest
testkey1=testvalue1
#test comment
testkey2=testvalue2
#test comment
testkey3=testvalue3
testkey4=200

[section1]
app_name=section1_IniConfigTest
testkey1=section1_testvalue1
testkey2=section1_testvalue2
testkey3=section1_testvalue3
testkey4=section1_200

运行效果:

C++实用工具类-ini配置文件解析

原文链接: https://www.cnblogs.com/flyingfox-cpp/p/17053922.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C++实用工具类-ini配置文件解析

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/310454

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月16日 上午11:36
下一篇 2023年2月16日 上午11:36

相关推荐