在程序開發中,配置文件扮演着很重要的角色,實現程序的靈活配置,方便不同環境下部署和使用,對於一些隨着外界環境變化的參數,直接寫入配置文件。本文將介紹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,請隨時聯繫博主修改更新,謝謝。