在程序开发中,配置文件扮演着很重要的角色,实现程序的灵活配置,方便不同环境下部署和使用,对于一些随着外界环境变化的参数,直接写入配置文件。本文将介绍boost总ini配置文件的读写。
初始化ini解析器
在程序开发中,文件读写是很重要的一个环节,同样,boost也提供了强大的文件读写功能,对于C++中经常使用的ini文件,boost直接提供了解析接口,使用者可以很方便的调用。
在boost中,解析ini文件的接口定义在如下文件中。
#include <boost/property_tree/ini_parser.hpp>
boost操作ini文件,是按照树形结构解析读取的,对应的树形结构解析接口位于如下头文件中。
#include <boost/property_tree/ptree.hpp>
初始化init文件的接口为 :read_ini,从boost源码可以看出,初始化流程如下:
- 打开文件流;
- 逐行读取文件,解析为property tree;
- 将解析后的property tree返回给使用者;
后续的读、写、修改都需要基于当前的property tree,该接口的实现源码如下:
/**
* Read INI from a the given file and translate it to a property tree.
* @note Clears existing contents of property tree. In case of error the
* property tree unmodified.
* @throw ini_parser_error In case of error deserializing the property tree.
* @param filename Name of file from which to read in the property tree.
* @param[out] pt The property tree to populate.
* @param loc The locale to use when reading in the file contents.
*/
template<class Ptree>
void read_ini(const std::string &filename,
Ptree &pt,
const std::locale &loc = std::locale())
{
std::basic_ifstream<typename Ptree::key_type::value_type>
stream(filename.c_str()); //open file
if (!stream)
BOOST_PROPERTY_TREE_THROW(ini_parser_error(
"cannot open file", filename, 0));
stream.imbue(loc);
try {
read_ini(stream, pt); //start parse ini file
}
catch (ini_parser_error &e) {
BOOST_PROPERTY_TREE_THROW(ini_parser_error(
e.message(), filename, e.line()));
}
}
/**
* Read INI from a the given stream and translate it to a property tree.
* @note Clears existing contents of property tree. In case of error
* the property tree is not modified.
* @throw ini_parser_error If a format violation is found.
* @param stream Stream from which to read in the property tree.
* @param[out] pt The property tree to populate.
*/
template<class Ptree>
void read_ini(std::basic_istream<
typename Ptree::key_type::value_type> &stream,
Ptree &pt)
{
typedef typename Ptree::key_type::value_type Ch;
typedef std::basic_string<Ch> Str;
const Ch semicolon = stream.widen(';');
const Ch hash = stream.widen('#');
const Ch lbracket = stream.widen('[');
const Ch rbracket = stream.widen(']');
Ptree local;
unsigned long line_no = 0;
Ptree *section = 0;
Str line;
// For all lines
while (stream.good())
{
// Get line from stream
++line_no;
std::getline(stream, line);
if (!stream.good() && !stream.eof())
BOOST_PROPERTY_TREE_THROW(ini_parser_error(
"read error", "", line_no));
// If line is non-empty
line = property_tree::detail::trim(line, stream.getloc());
if (!line.empty())
{
// Comment, section or key?
if (line[0] == semicolon || line[0] == hash)
{
// Ignore comments
}
else if (line[0] == lbracket)
{
// If the previous section was empty, drop it again.
if (section && section->empty())
local.pop_back();
typename Str::size_type end = line.find(rbracket);
if (end == Str::npos)
BOOST_PROPERTY_TREE_THROW(ini_parser_error(
"unmatched '['", "", line_no));
Str key = property_tree::detail::trim(
line.substr(1, end - 1), stream.getloc());
if (local.find(key) != local.not_found())
BOOST_PROPERTY_TREE_THROW(ini_parser_error(
"duplicate section name", "", line_no));
section = &local.push_back(
std::make_pair(key, Ptree()))->second;
}
else
{
Ptree &container = section ? *section : local;
typename Str::size_type eqpos = line.find(Ch('='));
if (eqpos == Str::npos)
BOOST_PROPERTY_TREE_THROW(ini_parser_error(
"'=' character not found in line", "", line_no));
if (eqpos == 0)
BOOST_PROPERTY_TREE_THROW(ini_parser_error(
"key expected", "", line_no));
Str key = property_tree::detail::trim(
line.substr(0, eqpos), stream.getloc());
Str data = property_tree::detail::trim(
line.substr(eqpos + 1, Str::npos), stream.getloc());
if (container.find(key) != container.not_found())
BOOST_PROPERTY_TREE_THROW(ini_parser_error(
"duplicate key name", "", line_no));
container.push_back(std::make_pair(key, Ptree(data)));
}
}
}
// If the last section was empty, drop it again.
if (section && section->empty())
local.pop_back();
// Swap local ptree with result ptree
pt.swap(local);
}
具体调用示例代码如下:
bool Init()
{
// 调用boost 文件系统接口,先检查文件是否存在。
if (!boost::filesystem::exists(this->strFileName))
{
cout << "file not exists!" << endl;
return false;
}
// 调用read_ini接口,将ini文件内容读入 root_node树节点中。
// root_node类型为:boost::property_tree::ptree
boost::property_tree::ptree root_node;
boost::property_tree::ini_parser::read_ini(this->strFileName, root_node);
return true;
}
写入ini文件
使用ofstream写入
使用ofstream对象写入,直接使用了C++写文件方式,示例代码如下:
boost::filesystem::ofstream ostream("config.ini", std::ios_base::out);
ostream << "[System] \n";
ostream << "ip=127.0.0.1\n";
ostream << "port=8080\n";
ostream << "[sdk]\n";
ostream << "path=C:\\log\n";
ostream.close();
文件样式:
使用boost接口写入
使用boost提供的API写入文件,需要用到boost::property_tree::ptree结构的 put 方法。示例代码如下:
bool UpdateItem(string strRoot_child_name,string value)
{
// put rootnode.childnode value
boost::property_tree::ptree root_node
// put第一个参数需要传入子节点和子节点下元素的名称,中间用.隔开,比如System.pwd
this->root_node.put<string>(strRoot_child_name, value);
write_ini("config.ini", root_node);
return true;
}
调用示例
config.UpdateItem("System.ip", "192.168.10.12");
config.UpdateItem("System.pwd", "admin_123455");
config.UpdateItem("ThirdPart.age", "10");
文件显示结果:
读取ini文件
读取ini文件也非常简单,需要使用boost提供的 get 函数读取对应的字段值,示例代码如下:
int Get_int_value(string node_name, string child_name)
{
boost::property_tree::ptree child_node;
// return child node
child_node = root_node.get_child(node_name);
// get child value
return child_node.get<int>(child_name);
}
修改ini文件
修改ini文件,同样需要用到boost库的put方法。前提是指定已经存在的节点和节点下面的字段名称。如果该字段存在,则修改,如果不存在,则新增。
bool UpdateItem(string strRoot_child_name, string value)
{
// put rootnode.childnode value
this->root_node.put<string>(strRoot_child_name, value);
write_ini("config.ini", root_node);
return true;
}
调用函数
config.UpdateItem("ThirdPart.age", "5555555555555");
修改结果:
以上是ini配置文件的创建、修改和新增操作,当然,按照模板,手动也可以增删改。每个人的使用习惯不同,可以按照各自习惯灵活使用。本文中测试代码,已经重新封装,直接链接即可使用,有需要可以参见博主的github仓库【最近刚刚开始玩git,后续所有代码将收录入仓库】,地址为:https://github.com/Marccp-code/boost,如果本文对你工作、学习等有帮助,请点赞支持我,若有错误或者bug,请随时联系博主修改更新,谢谢。