C++实用工具类-ini配置文件解析
dcc084fe-a937-4cf6-9b2d-f59765a1db19
ini配置文件介绍
一种常见的kv结构的配置文件
主要格式为 key=value
同时支持section, kv归属于一个section 如:
[default]
key1=value1
key2=value2
[section2]
key1=value1
key2=value2
注释, 支持#作为注释开始标记, 本实现仅支持 #单独一行的情况
实现方式
解析文件后按照状态流转进行读取, 如下:
全部读取文件内容到内存后, 按照状态流转一次循环全部字符完成解析,解析内容存放成2级map结构
section-->IniSection
IniSection内部存放 key->value map
并对外提供查询value接口
实现代码
代码目录:
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
运行效果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人