栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

C++下基于std标准库实现配置文件的读取

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++下基于std标准库实现配置文件的读取

在一般应用程序中,配置文件的格式有json、yaml、xml、ini等格式。这些格式对变量类型支持较为丰富,如int、string、double、array等。但使用这些格式的配置文件通常需要导入一些其他的外部库,如json、yaml、xml等格式;ini格式是windows系统内置的,但是放到liunx平台上就不支持;这些需要库依赖。采用使用的配置文件(json、yaml、xml、ini)支持域嵌套,但这在中小型程序中是非必须的,在以string做为key的配置文件中完全可以使用前缀(如 section.key来表示)。且,通过后处理,配置文件中以string表示的value可以转化为int、double、array等格式。也就是说,完全可以通过读取文本实现配置文件的加载调用。

同时,在网上的开源代码中也有许多c++脚本实现了key=value格式配置文件的读取。为此,博主针对于C++配置文件的读取,以std::map的方式进行了实现,代码保存为config.hpp,通过include后在项目中即可用。

本实现支持注释,支持默认配置,支持配置文件中多余的空格(使用trim函数过滤空格),支持配置文件的修改并(在本文最后一节实现,自定义类)。在https://c.zhizuobiao.com/c-18120300137/中以链表的形式实现了配置文件的读写,尽管不支持默认配置、注释与多余空格,但还是值得可以参考一下。


1、配置文件的格式

大部分配置文件的格式都是通过换行符来分割不同的配置项,通过#描述注释,通过key=value的格式进行配准项描述。具体可见下列示例代码

port=8080     #设置端口
host=http://www.baidu.com    #设置服务器路径
default_savename=down.zip    #设置默认保存文件的命令
cmd=wget [URL] -o [SAVENAME]
max_time=100  #设置cmd最大执行时间
2、基本思路

加载配置文件分四步实现,1、将配置文件读取为string;2、利用换行符分割多个配置项;3、利用#分割注释与配置项内容;4利用=分割key与value

为了实现这四步博主实现了四个主要函数。
get_configs函数:用于加载配置文件,调用其他函数处理string,生成std::map
Stringsplit函数:用于步骤2,将多行的配置文件分割开
Stringsplit2函数:用于步骤3与步骤4,分割#与=
trim函数:用于步骤1的get_configs函数中,去除key与value中多余的空格

此外还实现了三个辅助函数
string_replace函数:用于实现字符串模板中的替换
convertFromString函数:用于实现字符串类型转换,支持将string转换为int、flaot、double
read_config函数:用于安全读取配置项,简化读取代码。防止读取到未知配置项时报错

3、全部代码

代码保存为config.hpp即可,在个人项目中直接导入即可。

#include 
#include 
#include 
#include 
#include 
//-------------三个辅助函数-------------
//用于支持配置文件中的动态字符串 如 "wget {URL} -o down.zip"
std::string string_replace(std::string source, std::string find, std::string replace)
{
	std::string::size_type pos = 0;
	std::string::size_type a = find.size();
	std::string::size_type b = replace.size();
	while ((pos = source.find(find, pos)) != std::string::npos)
	{
		//source.replace(pos, a, replace);
		source.erase(pos, a);
		source.insert(pos, replace);
		pos += b;
	}
	return source;
}
//用于支持配置文件中的字符串转数字
template 
void convertFromString(T& value, const std::string& s) {
	std::stringstream ss(s);
	ss.precision(s.length());
	ss >> value;
}
//用于安全读取配置文件(只用config.find(key)->second写错了key会导致报错)
std::string read_config(std::map config, std::string key) {
	auto it = config.find(key);
	if (it == config.end()) {
		return "";
	}
	else {
		return it->second;
	}
}

//-------------四个主要函数-------------
//用于去除字符串多余的空格
std::string trim(std::string text)
{
	if (!text.empty())
	{
		text.erase(0, text.find_first_not_of(" nrt"));//去除字符串头部的空格
		text.erase(text.find_last_not_of(" nrt") + 1);//去除字符串尾部的空格
	}
	return text;
}
//用于支持将多行的配置文件分割开来
void Stringsplit(std::string str, const char split, std::vector& strList)
{
	std::istringstream iss(str);	// 输入流
	std::string token;			// 接收缓冲区
	while (getline(iss, token, split))	// 以split为分隔符
	{
		strList.push_back(token);
	}
}
//用于支持将 key=value 格式的配置文件分割开来(只分割一次)
void Stringsplit2(std::string str, const char split, std::vector& strList)
{
	//string str = "key=value1 value2 #notes";
	size_t pos = str.find(split); // 3
	if (pos>0&&pos get_configs(std::string fname) {
	std::string strdata;
	try {
		std::ifstream in(fname, std::ios::in);
		std::istreambuf_iterator beg(in), end;
		strdata = std::string(beg, end);
		in.close();
		if (strdata.length() < 10) {
			std::cout << fname << " context is not correct! " << std::endl;
		}
	}
	catch (...) {
		std::cout <<"Read " << fname << " error! " << std::endl;
	}

	std::vector strList;
	Stringsplit(strdata, 'n', strList);
	std::map maps;
	for (int i = 0;i < strList.size();i++) {
		std::vector tmp1,tmp2;
		Stringsplit2(strList[i], '#', tmp1);//用于清除注释  注释存储在trim(tmp1[1])中
		Stringsplit2(tmp1[0], '=', tmp2);//把字符串分为两节
		maps.insert({ trim(tmp2[0]),trim(tmp2[1]) });//去除字符串多余的空格(包含 nrt)
	}

	//添加默认配置
	//如果配置文件中的key都是正常设置了的,那么下面的insert代码都不会生效
	maps.insert({ "port","80" });
	maps.insert({ "host", "http://www.bing.com" });
	maps.insert({ "default_savename", "default_savename.zip" });
	maps.insert({ "cmd", "wget [URL] -o [SAVENAME]" });
	maps.insert({ "max_time", "1000" });
	return maps;
}
4、调用示例
#include 

extern std::map config = get_configs("config.txt");
int main(){
    std::string key="host";
    std::string value=config.find(key)->second;
    std::string value=read_config(config,key);
}
5、支持修改保存的配置文件类

通过自定义类,实现配置文件的加载、读取、修改与保存。

#include 
#include 
#include 
#include 
#include 
//-------------辅助函数-------------
//用于支持配置文件中的动态字符串 如 "wget {URL} -o down.zip"
std::string string_replace(std::string source, std::string find, std::string replace)
{
	std::string::size_type pos = 0;
	std::string::size_type a = find.size();
	std::string::size_type b = replace.size();
	while ((pos = source.find(find, pos)) != std::string::npos)
	{
		//source.replace(pos, a, replace);
		source.erase(pos, a);
		source.insert(pos, replace);
		pos += b;
	}
	return source;
}
//用于支持配置文件中的字符串转数字
template 
void convertFromString(T& value, const std::string& s) {
	std::stringstream ss(s);
	ss.precision(s.length());
	ss >> value;
}

//用于去除字符串多余的空格
std::string trim(std::string text)
{
	if (!text.empty())
	{
		text.erase(0, text.find_first_not_of(" nrt"));//去除字符串头部的空格
		text.erase(text.find_last_not_of(" nrt") + 1);//去除字符串尾部的空格
	}
	return text;
}
//用于支持将多行的配置文件分割开来
void Stringsplit(std::string str, const char split, std::vector& strList)
{
	std::istringstream iss(str);	// 输入流
	std::string token;			// 接收缓冲区
	while (getline(iss, token, split))	// 以split为分隔符
	{
		strList.push_back(token);
	}
}
//用于支持将 key=value 格式的配置文件分割开来(只分割一次)
void Stringsplit2(std::string str, const char split, std::vector& strList)
{
	//string str = "key=value1 value2 #notes";
	size_t pos = str.find(split); // 3
	if (pos>0&&pos maps;
	TxtConfig(std::string fname) {
		parse_file(fname);
	}
	//解析配置文件,并添加默认配置
	void parse_file(std::string fname) {
		std::string strdata;
		try {
			std::ifstream in(fname, std::ios::in);
			std::istreambuf_iterator beg(in), end;
			strdata = std::string(beg, end);
			in.close();
			if (strdata.length() < 10) {
				std::cout << fname << " context is not correct! " << std::endl;
			}
		}
		catch (...) {
			std::cout << "Read " << fname << " error! " << std::endl;
		}

		std::vector strList;
		Stringsplit(strdata, 'n', strList);
		//std::map maps;
		for (int i = 0;i < strList.size();i++) {
			std::vector tmp1, tmp2;
			Stringsplit2(strList[i], '#', tmp1);//用于清除注释  注释存储在trim(tmp1[1])中
			Stringsplit2(tmp1[0], '=', tmp2);//把字符串分为两节
			maps.insert({ trim(tmp2[0]),trim(tmp2[1]) });//去除字符串多余的空格(包含 nrt)
		}

		//添加默认配置
		//如果配置文件中的key都是正常设置了的,那么下面的insert代码都不会生效
	    maps.insert({ "port","80" });
	    maps.insert({ "host", "http://www.bing.com" });
    	maps.insert({ "default_savename", "default_savename.zip" });
	    maps.insert({ "cmd", "wget [URL] -o [SAVENAME]" });
    	maps.insert({ "max_time", "1000" });
		return ;
	}
	//用于输出配置文件,或保存配置文件
	void print(std::string file_name = "") {
		std::map::iterator iter1;
		std::string str = "";
		for (iter1 = maps.begin();iter1 != maps.end();iter1++)
		{
			str += iter1->first + "=" + iter1->second + "n";
		}
		if (file_name.length() == 0) {
			std::cout << str;
		}
		else {
			// 向文件里写数据
			std::ofstream os;     //创建一个文件输出流对象
			// os.open("../books.txt", std::fstream::out|std::fstream::app);//将对象与文件关联, app就是每次在文件后面继续添加内容
			os.open(file_name, std::fstream::out);//将对象与文件关联
			os << str;   //将输入的内容放入txt文件中
			os.close();
		}
	}
	//用于安全读取配置文件(只用config.find(key)->second写错了key会导致报错)
	std::string read( std::string key,std::string default="") {
		auto it = maps.find(key);
		if (it == maps.end()) {
			return default;
		}
		else {
			return it->second;
		}
	}
	//用于配置文件赋值,当key不存在时为新增
	void set(std::string key, std::string value) {
		maps[key] = value;
	}
};

用法


TxtConfig config = TxtConfig("config.txt");
config.print();
config.set("sss", "aaaaaaaa");
std::string port_str = config.read("port");

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/998753.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号