std::map> parseIni(const std::string& filename) {
std::map> config;
std::ifstream file(filename);
std::string line;
std::string section;
while (std::getline(file, line)) {
// 去除首尾空白
size_t first = line.find_first_not_of(" \t");
size_t last = line.find_last_not_of(" \t");
if (first == std::string::npos) continue;
line = line.substr(first, (last - first + 1));
// 跳过注释
if (line[0] == '#' || line[0] == ';') continue;
// 匹配节 [section]
if (line[0] == '[') {
size_t end = line.find(']');
if (end != std::string::npos) {
section = line.substr(1, end - 1);
}
} else {
// 解析 key=value
size_t sep = line.find('=');
if (sep != std::string::npos) {
std::string key = line.substr(0, sep);
std::string value = line.substr(sep + 1);
// 去除key和value的空白
key.erase(key.find_last_not_of(" \t") + 1);
value.erase(0, value.find_first_not_of(" \t"));
config[section][key] = value;
}
}
}
return config;}
使用第三方库简化操作
对于更复杂的需求,推荐使用成熟的小型库,避免重复造轮子。
推荐库:
-
SimpleIni:跨平台、单头文件、支持ASCII/Unicode
-
iniparser:C语言编写,轻量易集成
以 SimpleIni 为例,使用步骤:
- 下载 SimpleIni.h 和对应源文件
- 包含头文件并使用类 CSimpleIniA
- 加载文件并查询值
示例:
#include "SimpleIni.h"
CSimpleIniA ini;
ini.SetUnicode();
ini.LoadFile("config.ini");
const char* value = ini.GetValue("database", "host", "localhost"); // 默认值
int port = atoi(ini.GetValue("database", "port", "3306"));
读取后的数据使用建议
解析完成后,应根据实际类型进行转换。常见做法包括:
- 字符串直接使用
- 数值类型用 std::stoi、std::stod 等转换
- 布尔值可约定 "true"/"false" 或 "1"/"0"
- 提供默认值机制,防止缺失配置导致崩溃
可封装一个配置管理类,统一对外提供接口,例如:
class ConfigManager {
public:
std::string getString(const std::string& sec, const std::string& key, const std::string& def) {
auto it = data.find(sec);
if (it != data.end()) {
auto kv = it->second.find(key);
if (kv != it->second.end()) return kv->second;
}
return def;
}
int getInt(const std::string& sec, const std::string& key, int def) {
std::string val = getString(sec, key, "");
try { return std::stoi(val); }
catch (...) { return def; }
}private:
std::map<:string std::map std::string>> data;
};
基本上就这些。手动解析适合学习和简单场景,第三方库更适合生产环境。选择哪种方式取决于项目规模和维护要求。