简易C++配置文件读取

今天分享一个简易的C++配置文件读取器。大概能读取的格式如下:

[group1]
arg1=1
arg2=2.0
arg3=D:/123

#我是一行注释
[group2]
arg1=woshiniyeye

大概说下几个点,不懂的自行百度(大佬当我放屁,没啥高级货):

  • 完全使用标准库实现(说的屁话)
  • 单例模式
  • call once初始化
  • 参数可以给默认值,防止没读取到
  • 值中间不能有空格
  • 组名不能重复,每个组当中的节点名不能重复
  • 值不支持中文

直接上代码,先来头文件

#pragma once
#include <map>
#include <string>
#include <mutex>
#include <memory>
/*****读取具有如下形式的配置文件****************
* [group1]
* arg_float=0.2
* arg_int=1
* arg_string=12345
* #我是一行注释
* [group2]
* arg_666 = 我是你大哥
* ....
*
* 每个中括号表示一个section,不能重复出现
* =号左边的字段,在每个section中不能重复出现
* setcion、字段、值不能出现空格和中文字符
* # 或 ; 代表注释行,只能一行一行注释
******************************************/

class Config
{
    
    
private:
	std::string escapeString(const std::string& src);
	template<typename T>
	T getParam(const std::map<std::string, std::string>& params, const std::string param_name, T init_val);
	std::map<std::string, std::map<std::string, std::string>> m_params;
	void getGroup(std::map<std::string, std::string>& param,std::string name);

private:
	Config() = default;
	Config(const Config&) = delete;
	Config& operator=(const Config&) = delete;
	static std::shared_ptr<Config> m_instance;
public:
	void readConfig(const char* filename);
	static std::shared_ptr<Config> instance();//实例接口
public:
	float arg_float;
	int arg_int;
	std::string arg_string;
	std::string arg_666;


};




然后是CPP实现

#include "config.h"
#include <istream>
#include <iostream>
#include <fstream>
#include <sstream>

#define printf(str,...) printf(str,__VA_ARGS__);printf("\n");

void config_expfunc(const char* expr, const char* func, const char* file, int line)
{
    
    
	printf("%s %s %s %d", expr, func, file, line);
	throw std::exception("解析错误");
}

#define CONFIG_Assert( expr ) do { if(!!(expr)) ; else config_expfunc(#expr,__FUNCTION__, __FILE__, __LINE__ ); } while(0)


std::string Config::escapeString(const std::string& src)
{
    
    
	std::string dst;
	for (std::size_t i = 0; i < src.size(); ++i)
		if (src[i] > ' ' && src[i] <= 'z')
			dst += src[i];
	return dst;
}
void Config::getGroup(std::map<std::string, std::string>& param,std::string name)
{
    
    
	param.clear();
	if (m_params.find(name) != m_params.end())
	{
    
    
		param = m_params[name];
	}
}
template<typename T>
T Config::getParam(const std::map<std::string, std::string>& params, const std::string param_name, T init_val)
{
    
    
	std::map<std::string, std::string>::const_iterator it = params.find(param_name);
	if (it != params.end()) {
    
    
		std::stringstream ss(it->second);
		ss >> init_val;
		printf("解析配置文件时,[%s] = %s", param_name.c_str(), ss.str().c_str());

	} else {
    
    
		std::stringstream ss;
		ss << init_val;

		printf("解析配置文件时,未设置 [%s],使用默认值 %s", param_name.c_str(), ss.str().c_str());

	}
	return init_val;
}
template<>
std::string Config::getParam<std::string>(const std::map<std::string, std::string>& params, const std::string param_name, std::string init_val)
{
    
    
	std::map<std::string, std::string>::const_iterator it = params.find(param_name);
	if (it != params.end()) {
    
    
		init_val = it->second;
		printf("解析配置文件时,[%s] = %s", param_name.c_str(), init_val.c_str());

	} else
	{
    
    
		printf("解析配置文件时,未设置 [%s],使用默认值 %s", param_name.c_str(), init_val.c_str());

	}

	return init_val;
}
void Config::readConfig(const char* filename)
{
    
    
	
	//print_log(LOG_INFO, "loading config");
	m_params.clear();
	std::filebuf in;
	if (!in.open(filename, std::ios::in))
	{
    
    
		//print_log(LOG_ERROR, "failed to read config file,全部使用默认值");
		printf("failed to read config file,全部使用默认值");
		
	} else
	{
    
    
		try
		{
    
    
			std::string last_section;
			std::istream ifile(&in);
			for (std::string line; std::getline(ifile, line);) {
    
    
				line = escapeString(line);
				if (line.empty()) continue;
				switch (line[0]) {
    
    
				case '\0': break;
				case '#': break;
				case ';': break;
				case '[': {
    
    
					const size_t layer_type_size = line.find("]") - 1;					
					CONFIG_Assert(layer_type_size < line.size());
					std::string layer_type = line.substr(1, layer_type_size);
					CONFIG_Assert(m_params.find(layer_type) == m_params.end());
					m_params[layer_type] = std::map<std::string, std::string>(); //读取分组
					last_section = layer_type;
				}
						break;
				default:
					//读取每个分组里的节点内容
					const size_t separator_index = line.find('=');
					CONFIG_Assert(separator_index < line.size());
					CONFIG_Assert(!m_params.empty());
					if (separator_index != std::string::npos) {
    
    
						std::string name = line.substr(0, separator_index);
						std::string value = line.substr(separator_index + 1, line.size() - (separator_index + 1));
						name = escapeString(name);
						value = escapeString(value);
						if (name.empty() || value.empty()) continue;
						auto& section = m_params[last_section];
						CONFIG_Assert(section.find(name) == section.end());
						section[name] = value;
					}
					break;
				}
			}
		} catch (std::exception& msg)
		{
    
    

			printf("配置文件解析失败 详细信息:%s", msg.what());
			throw std::exception("配置文件解析失败");

		} catch (...)
		{
    
    
			printf("配置文件解析失败");
			throw std::exception("配置文件解析失败");
		}
	}


	printf("读取配置文件节点数据");
	//section
	std::map<std::string, std::string> param;
	getGroup(param,"group1");

	arg_float = getParam(param, "arg_float", 0.2);
	arg_int = getParam(param, "arg_int", 1);
	arg_string = getParam<std::string>(param, "arg_string", "12345");
	
	
	getGroup(param, "group2");

	arg_666 = getParam<std::string>(param, "arg_666", "我是你大哥");

	printf( "配置文件读取成功");

}
template class std::unique_ptr<Config>;
std::shared_ptr<Config> Config::m_instance;
std::shared_ptr<Config> Config::instance()
{
    
    
	static std::once_flag once_flag;
	std::call_once(once_flag, [&]()
		{
    
    
			m_instance.reset(new Config);
			m_instance->readConfig("config.ini");			
		});
	return m_instance;
}

最后是一个测试文件

#include <iostream>
#include "config.h"

using namespace std;
int main()
{
    
    
    auto& config = *Config::instance();
    cout << config.arg_666 << endl;
    
}

如果读取配置文件失败的话,会全部使用默认值,

在这里插入图片描述

如果读取配置文件成功的话,是这样的:

在这里插入图片描述

说明一下

  • -其实这段代码是我从opencv源码里面拿出来改的
  • 上面的CONFIG_Assert 、printf宏完全是为了方便贴代码展示改的
  • 细心的朋友发现了有个print_log注释,那是另外一个简易日志库,叫做zlog,可以gayhub搜搜,可以的话,我下次再写一篇简易日志库凑凑博客数。

Guess you like

Origin blog.csdn.net/qq_16952303/article/details/115014481