Boost學習之讀寫ini文件

在程序開發中,配置文件扮演着很重要的角色,實現程序的靈活配置,方便不同環境下部署和使用,對於一些隨着外界環境變化的參數,直接寫入配置文件。本文將介紹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源碼可以看出,初始化流程如下:

  1. 打開文件流;
  2. 逐行讀取文件,解析爲property tree
  3. 將解析後的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,請隨時聯繫博主修改更新,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章