boost的property_tree组件

1.1 缘起

stl中对线性表有充分的实现,无论是vector还是list都是典型的线性表,即便是set和map,尽管实现上采用了诸如红黑树之类的树形结构,但那仅仅是为了快速检索的需要,从语义上来说它们依旧是线性表,无法表达目录树这种树形结构。boost中的property_tree可以看做是对树形结构实现的补充,我们大可把它扩展应用到各种需要树形结构的地方。

当我们拥有了一个树性结构以后,那么也就同时拥有了树形结构中的每个节点的访问权,很自然的,我们希望每个节点都可以方便的检索(类似于输入一个文件路径就快速的定位到文件),同时我们也希望能在节点上存储点什么以支持各种实际的应用。这里可以和MFC的树形控件CTreeCtrl做一下类比,MFC中的CTreeCtrl不支持类似于路径的检索,好在CTreeCtrl的节点中有一个字符串类型的名称,使得我们可以通过字符串和不断的GetChildItem,GetNextItem自己去检索定位;同时它的每个节点都可以存储一个int数据,并通过方法SetItemData和GetItemData进行读取,然后再通过这个int数据再关联到那些比int更复杂的数据结构。这种通过int数据二次关联的实现是能够工作的,只是有些繁琐。而在boost的泛型世界里,再如此硬编码一个类型肯定会被人笑话的。标准的做法是为树形结构的每个节点关联一个模板Key类型的索引数据和一个Data类型的存储数据,至于Key和Data类型是什么,爱是谁是谁。这个被关联的Data类型的数据就是节点的属性(property)。说了这么多,我们终于可以解开property_tree的真面目了:

    template<class Key, class Data, class KeyCompare>
    class basic_ptree
    {
        typedef basic_ptree<Key, Data, KeyCompare> self_type;

    public:
        // Basic types
        typedef Key                                  key_type;
        typedef Data                                 data_type;
        typedef KeyCompare                           key_compare;

        // Container view types
        typedef std::pair<const Key, self_type>      value_type;
        typedef std::size_t                          size_type;

    private:
        // Hold the data of this node
        data_type m_data;
        // Hold the children - this is a void* because we can't complete the
        // container type within the class.
        void* m_children;
    };

其中的m_children又到底是什么类型呢?需要看下面的实现代码:

    template<class K, class D, class C> inline
    basic_ptree<K, D, C>::basic_ptree()
        : m_children(new typename subs::base_container)
    {
    }

    template <class K, class D, class C>
    struct basic_ptree<K, D, C>::subs
    {
        struct by_name {};
        // The actual child container.
        typedef multi_index_container<value_type,
            multi_index::indexed_by<
                multi_index::sequenced<>,
                multi_index::ordered_non_unique<multi_index::tag<by_name>,
                    multi_index::member<value_type, const key_type,
                                        &value_type::first>,
                    key_compare
                >
            >
        > base_container;
    };

multi_index也是boost中的一个组件,作用是为一个数据结构提供多种方式的索引,我们先不纠结于multi_index的细节,可以把它简化为list<value_type>,而value_type又是一个std::pair<const Key, self_type>。好了,整个树形的数据结构就串起来了,每个节点都保存一个data_type m_data,即一个Data类型的属性值,同时还保存一个list<value_type>,即以Key标识的所有子节点列表。

1.2 遍历

了解了property_tree的数据结构,树的遍历就很简单了。

首先,我们可以通过data方法得到节点上的属性值:

    template<class K, class D, class C> inline
    typename basic_ptree<K, D, C>::data_type &
        basic_ptree<K, D, C>::data()
    {
        return m_data;
    }

其次,我们可以构造所有子节点的迭代器:

    template <class K, class D, class C>
    class basic_ptree<K, D, C>::iterator : public boost::iterator_adaptor<
        iterator, typename subs::base_container::iterator, value_type>
    {
        friend class boost::iterator_core_access;
        typedef boost::iterator_adaptor<
            iterator, typename subs::base_container::iterator, value_type>
            baset;
    public:
        typedef typename baset::reference reference;
        iterator() {}
        explicit iterator(typename iterator::base_type b)
            : iterator::iterator_adaptor_(b)
        {}
        reference dereference() const
        {
            // multi_index doesn't allow modification of its values, because
            // indexes could sort by anything, and modification screws that up.
            // However, we only sort by the key, and it's protected against
            // modification in the value_type, so this const_cast is safe.
            return const_cast<reference>(*this->base_reference());
        }
    };

基本上直接封装内部数据结构的迭代器就可以了。剩下的就是stl的迭代器的标准用法了。

现在,我们可以直接写出property_tree的遍历函数了,这里采用递归处理,以树形结构的打印为例,同时为了打印的方便,我们采用ptree:

//typedef basic_ptree<std::string, std::string> ptree;
void print(ostream& os, const ptree& pt, int tab)
{
	tab += 2;
	for (ptree::const_iterator iter = pt.begin(); iter != pt.end(); ++iter)
	{
		os << string(tab, ' ');
		os << "{" << iter->first << "}" << "[" << iter->second.data() << "]\n";
		print(os, iter->second, tab);
	}
}

这里处理了tab的缩进,为什么一上来就+2?因为最外层的根节点其实是文档本身,而我们一般会跳过它。

1.3 Xml文件的读取

从property_tree的实现代码可以看到,其内部的xml解析采用了rapidxml,明晃晃的rapidxml.hpp是那么的显眼。这个号称是执行最快的xml解析的实现,既然要快,就不可能像xerces那样的庞大臃肿,步履蹒跚,当然也不可能象后者一样的面面俱到。(私下以为,如果property_tree敢用xerces,它也不可能被boost通过,呵呵)。

实现细节rapidxml被很好地封装起来,以保证未来实现部分的可替换性。对外直接给了read_xml和write_xml两个方法处理xml文件的读写,这里先看看read_xml方法:

    template<class Ptree>
    void read_xml(const std::string &filename,
                  Ptree &pt,
                  int flags = 0,
                  const std::locale &loc = std::locale())
    {
        BOOST_ASSERT(validate_flags(flags));
        std::basic_ifstream<typename Ptree::key_type::value_type>
            stream(filename.c_str());
        if (!stream)
            BOOST_PROPERTY_TREE_THROW(xml_parser_error(
                "cannot open file", filename, 0));
        stream.imbue(loc);
        read_xml_internal(stream, pt, flags, filename);
    }

好了,是时候写出property_tree读取xml的示例代码了:

void read_xml_demo()
{
	try
	{
		ptree doc;
		read_xml("config/config.xml", doc, xml_parser::trim_whitespace);

		print(cout, doc, -2);
	}
	catch (xml_parser_error& e)
	{
		cout << e.what() << endl;
	}
	catch (std::exception& e)
	{
		cout << e.what() << endl;
	}
}

代码非常简单,不解释了。这里示例的config.xml是下面的内容:

<?xml version="1.0" encoding="GB2312"?>
<config>
	<main title="windows" icon="main.ico">
	<!-- Main Fisrt Comment -->
	<!-- Main Second Comment -->
	</main>
	<paths name="init">
		<!-- Paths Comment -->
		<path level="1">a.ini</path>
		<path level="2">b.ini</path>
		<path level="3">c.ini</path>
	</paths>
	<links key="<>">
		<!-- Links Comment -->
		<link level="1">www.sina.com</link>
		<link level="2">www.sohu.com</link>
		<link level="3">www.163.com</link>
	</links>
</config>

但是你能想到会打印出什么东西吗?这里其实有一个非常严重的问题:数据结构的不对等。

我们知道,xml本身结构是比较复杂的,节点的类型有:

    enum node_type
    {
        node_document,      //!< A document node. Name and value are empty.
        node_element,       //!< An element node. Name contains element name. Value contains text of first data node.
        node_data,          //!< A data node. Name is empty. Value contains data text.
        node_cdata,         //!< A CDATA node. Name is empty. Value contains data text.
        node_comment,       //!< A comment node. Name is empty. Value contains comment text.
        node_declaration,   //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes.
        node_doctype,       //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text.
        node_pi             //!< A PI node. Name contains target. Value contains instructions.
    };

(摘自rapidxml.hpp。)

每个元素下还有一组属性和一个可选的text。

但是我们知道property_tree的数据结构是比较简单的,并不能支持xml这样的复杂结构。这个问题我估计也是让property_tree的作者很纠结的问题,被逼无奈之下,他采用了一个能工作但很难看的解决方案:字符串的私有协议。简单的说协议有两条:

  • 字符串”<xmlattr>” 表示该节点是xml属性
  • 字符串”<xmlcomment>” 表示该节点是xml注释

这样,当我们看到下面的输出结果就不会感到惊讶了:

{config}[]
  {main}[]
    {<xmlattr>}[]
      {title}[windows]
      {icon}[main.ico]
    {<xmlcomment>}[ Main Fisrt Comment ]
    {<xmlcomment>}[ Main Second Comment ]
  {paths}[]
    {<xmlattr>}[]
      {name}[init]
    {<xmlcomment>}[ Paths Comment ]
    {path}[a.ini]
      {<xmlattr>}[]
        {level}[1]
    {path}[b.ini]
      {<xmlattr>}[]
        {level}[2]
    {path}[c.ini]
      {<xmlattr>}[]
        {level}[3]
  {links}[]
    {<xmlattr>}[]
      {key}[<>]
    {<xmlcomment>}[ Links Comment ]
    {link}[www.sina.com]
      {<xmlattr>}[]
        {level}[1]
    {link}[www.sohu.com]
      {<xmlattr>}[]
        {level}[2]
    {link}[www.163.com]
      {<xmlattr>}[]
        {level}[3]

1.4 其它文件格式的读取

我在文章的开头就说过了property_tree是为树形结构而生的,而不仅仅只为xml而生的,因此property_tree还支持json、info、ini几种文件格式。了解了xml文件的读取后,这几种文件格式的读取也就比较简单了。

读取这三种文件的示例代码如下:

void read_json_demo()
{
	try
	{
		ptree doc;
		read_json("config/config.json", doc);

		print(cout, doc, -2);
	}
	catch (json_parser_error& e)
	{
		cout << e.what() << endl;
	}
	catch (std::exception& e)
	{
		cout << e.what() << endl;
	}
};

void read_ini_demo()
{
	try
	{
		ptree doc;
		read_ini("config/config.ini", doc);

		print(cout, doc, -2);
	}
	catch (json_parser_error& e)
	{
		cout << e.what() << endl;
	}
	catch (std::exception& e)
	{
		cout << e.what() << endl;
	}
};

void read_info_demo()
{
	try
	{
		ptree doc;
		read_info("config/config.info", doc);

		print(cout, doc, -2);
	}
	catch (json_parser_error& e)
	{
		cout << e.what() << endl;
	}
	catch (std::exception& e)
	{
		cout << e.what() << endl;
	}
};

如你所见,几乎是一个模子倒出来的,除了调用的读取方法略有区别。

这里统一给出以上代码所需要引用的头文件和命名空间:

#include <iostream>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/info_parser.hpp>

using namespace boost;
using namespace std;

using namespace boost::property_tree;

为了节省点读者的时间,我就再费点事,把三个格式的示例文件都贴出来吧。

示例文件config.json如下:

{
  "config" :
  {
    "main" :
    {
      "title" : "windows",
      "icon" : "main.ico",
      "comment" : "Main Fisrt Comment",
      "comment" : "Main Second Comment"
    },

    "paths" :
    {
      "name" : "init",
      "comment" : "Paths Comment",
      "path" :
      {
        "level" : "1",
        "text" : "a.ini"
      },
      "path" :
      {
        "level" : "2",
        "text" : "b.ini"
      },
      "path" :
      {
        "level" : "3",
        "text" : "c.ini"
      }
    },

    "links" :
    {
      "key" : "<>",
      "comment" : "Links Comment",
      "link" :
      {
        "level" : "1",
        "text" : "www.sina.com"
      },
      "link" :
      {
        "level" : "2",
        "text" : "www.sohu.com"
      },
      "link" :
      {
        "level" : "3",
        "text" : "www.163.com"
      }
    }
  }
}

示例文件config.info如下:

config
{
  main
  {
    title windows
    icon main.ico
    comment Main Fisrt Comment
    comment Main Second Comment
  }

  paths
  {
    name init
    comment Paths Comment
    path
    {
      level 1
      text a.ini
    },
    path
    {
      level 2
      text b.ini
    },
    path
    {
      level 3
      text c.ini
    }
  }

  links
  {
    key <>
    comment Links Comment
    link
    {
      level 1
      text www.sina.com
    }
    link
    {
      level 2
      text www.sohu.com
    }
    link
    {
      level 3
      text www.163.com
    }
  }
}

示例文件config.ini如下:

[config.main]
title="windows"
icon="main.ico"

[config.paths]
name="init"

[config.paths.path1]
level="1"
text=a.ini

[config.paths.path2]
level="2"
text=b.ini

[config.paths.path3]
level="3"
text=c.ini

[config.links]
key="<>"

[config.links.link1]
level="1"
text=www.sina.com

[config.links.link2]
level="2"
text=www.sohu.com

[config.links.link3]
level="3"
text=www.163.com

1.5 路径和访问

了解了xml文件的读取,下面就要关心树形结构的访问了。在文章的开头我说过,property_tree支持类似于路径的快速访问机制,这是通过path_type实现的:

    template <typename Key>
    struct path_of;

    template <typename Ch, typename Traits, typename Alloc>
    struct path_of< std::basic_string<Ch, Traits, Alloc> >
    {
        typedef std::basic_string<Ch, Traits, Alloc> _string;
        typedef string_path< _string, id_translator<_string> > type;
    };

    typedef typename path_of<Key>::type          path_type;

我们看到,path_type特化后其实就是一个字符串,与我们熟知的目录路径是一致的。我们首先看一下如何根据path定位?这可以调试get_child函数的调用堆栈:

    template<class K, class D, class C>
    basic_ptree<K, D, C> &
        basic_ptree<K, D, C>::get_child(const path_type &path)
    {
        path_type p(path);
        self_type *n = walk_path(p);
        if (!n) {
            BOOST_PROPERTY_TREE_THROW(ptree_bad_path("No such node", path));
        }
        return *n;
    }

    template<class K, class D, class C>
    basic_ptree<K, D, C> *
    basic_ptree<K, D, C>::walk_path(path_type &p) const
    {
        if(p.empty()) {
            // I'm the child we're looking for.
            return const_cast<basic_ptree*>(this);
        }
        // Recurse down the tree to find the path.
        key_type fragment = p.reduce();
        const_assoc_iterator el = find(fragment);
        if(el == not_found()) {
            // No such child.
            return 0;
        }
        // Not done yet, recurse.
        return el->second.walk_path(p);
    }

    template<class K, class D, class C> inline
    typename basic_ptree<K, D, C>::const_assoc_iterator
        basic_ptree<K, D, C>::find(const key_type &key) const
    {
        return const_assoc_iterator(subs::assoc(this).find(key));
    }

        static const by_name_index& assoc(const self_type *s) {
            return ch(s).BOOST_NESTED_TEMPLATE get<by_name>();
        }

        static const base_container& ch(const self_type *s) {
            return *static_cast<const base_container*>(s->m_children);
        }

  template<typename Tag>
  const typename index<Tag>::type& get()const BOOST_NOEXCEPT
  {
    return *this;
  }

我们看到,第一个函数get_child会调用walk_path进行实际的检索,而第二个函数walk_path会将这个工作转给find,第三个函数find则直接找节点内部的那个子节点列表数据结构了。

了解了get_child函数,我们再看add函数:

    template<class K, class D, class C>
    template<class Type> inline
    basic_ptree<K, D, C> & basic_ptree<K, D, C>::add(
        const path_type &path, const Type &value)
    {
        return add(path, value,
                   typename translator_between<data_type, Type>::type());
    }

    template<class K, class D, class C>
    template<class Type, typename Translator> inline
    basic_ptree<K, D, C> & basic_ptree<K, D, C>::add(
        const path_type &path, const Type &value, Translator tr)
    {
        self_type &child = add_child(path, self_type());
        child.put_value(value, tr);
        return child;
    }

    template<class K, class D, class C>
    basic_ptree<K, D, C> &
        basic_ptree<K, D, C>::add_child(const path_type &path,
                                        const self_type &value)
    {
        path_type p(path);
        self_type &parent = force_path(p);
        // Got the parent.
        key_type fragment = p.reduce();
        return parent.push_back(value_type(fragment, value))->second;
    }

第一个add函数增加了translator参数,第二个add函数先用add_child增加子节点,再用put_value写属性值,第三个add_child函数,代码也比较简单,在路径定位后,在父节点的数据结构中增加value_type即可。

注意到上面写节点属性值用到了put_value方法,为什么不直接用data方法?我们知道,property_tree中节点上的属性数据可以用方法data直接访问,但我们读写的可不一定是一定是Data类型的数据,因此这里就需要put和get的泛型封装了。

首先看看get,我们根据get函数执行的调用堆栈,依此找到了以下函数:

    template<class K, class D, class C>
    template<class Type> inline
    Type basic_ptree<K, D, C>::get_value() const
    {
        return get_value<Type>(
            typename translator_between<data_type, Type>::type());
    }

    template<class K, class D, class C>
    template<class Type, class Translator>
    typename boost::enable_if<detail::is_translator<Translator>, Type>::type
    basic_ptree<K, D, C>::get_value(Translator tr) const
    {
        if(boost::optional<Type> o = get_value_optional<Type>(tr)) {
            return *o;
        }
        BOOST_PROPERTY_TREE_THROW(ptree_bad_data(
            std::string("conversion of data to type \"") +
            typeid(Type).name() + "\" failed", data()));
    }

    template<class K, class D, class C>
    template<class Type, class Translator> inline
    optional<Type> basic_ptree<K, D, C>::get_value_optional(
                                                Translator tr) const
    {
        return tr.get_value(data());
    }

        boost::optional<E> get_value(const internal_type &v) {
            std::basic_istringstream<Ch, Traits, Alloc> iss(v);
            iss.imbue(m_loc);
            E e;
            customized::extract(iss, e);
            if(iss.fail() || iss.bad() || iss.get() != Traits::eof()) {
                return boost::optional<E>();
            }
            return e;
        }

第一个函数get_value为函数调用增加了translator参数。第二个函数get_value通过boost::optional增加了节点是否存在的判断。第三个函数get_value_optional增加了data()参数,并让Translator去解决无问题,第四个函数get_value是在Translator内部对data()进行最后的转换。

再来看put函数:

    template<class K, class D, class C>
    template<class Type> inline
    basic_ptree<K, D, C> & basic_ptree<K, D, C>::put(
        const path_type &path, const Type &value)
    {
        return put(path, value,
                   typename translator_between<data_type, Type>::type());
    }

    template<class K, class D, class C>
    template<class Type, typename Translator>
    basic_ptree<K, D, C> & basic_ptree<K, D, C>::put(
        const path_type &path, const Type &value, Translator tr)
    {
        if(optional<self_type &> child = get_child_optional(path)) {
            child.get().put_value(value, tr);
            return *child;
        } else {
            self_type &child2 = put_child(path, self_type());
            child2.put_value(value, tr);
            return child2;
        }
    }

    template<class K, class D, class C>
    template<class Type, class Translator>
    void basic_ptree<K, D, C>::put_value(const Type &value, Translator tr)
    {
        if(optional<data_type> o = tr.put_value(value)) {
            data() = *o;
        } else {
            BOOST_PROPERTY_TREE_THROW(ptree_bad_data(
                std::string("conversion of type \"") + typeid(Type).name() +
                "\" to data failed", boost::any()));
        }
    }

            boost::optional<internal_type> put_value(const E &v) {
            std::basic_ostringstream<Ch, Traits, Alloc> oss;
            oss.imbue(m_loc);
            customized::insert(oss, v);
            if(oss) {
                return oss.str();
            }
            return boost::optional<internal_type>();
        }

第一个put函数增加了translator参数,第二个put函数通过get_child_optional增加了节点是否存在的判断,第三个put_value函数让Translator去解决问题,第四个put_value是在Translator内部对数据进行最后的转换。

本节的测试代码如下:

void visit_xml_demo(ptree& doc)
{
	ptree root = doc.get_child("config.main");
	root.add("<xmlattr>.ver", "1");

	string title = root.get<string>("<xmlattr>.title");
	assert(title == "windows");
	int ver = root.get<int>("<xmlattr>.ver");
	assert(ver == 1);
	root.put("<xmlattr>.ver", 2);
}

代码比较简单,多跟踪几遍就一清二楚了。

1.6 xml文件的写入

了解了xml文件的读取,写入还很困难吗?确实,不过是read_xml和write_xml的区别而已:

    template<class Ptree>
    void write_xml(const std::string &filename,
                   const Ptree &pt,
                   const std::locale &loc = std::locale(),
                   const xml_writer_settings<
                       typename Ptree::key_type
                   > & settings = xml_writer_settings<typename Ptree::key_type>())
    {
        std::basic_ofstream<typename Ptree::key_type::value_type>
            stream(filename.c_str());
        if (!stream)
            BOOST_PROPERTY_TREE_THROW(xml_parser_error(
                "cannot open file", filename, 0));
        stream.imbue(loc);
        write_xml_internal(stream, pt, filename, settings);
    }

如果仅仅是xml的read_xml然后write_xml确实没什么麻烦的,因为read_xml的时候其实已经建立的整个树形结构,然后再保存就可以了。这里真正需要注意的是如果没有xml文件呢?该如何凭空构造一个树形结构?通过对路径和访问一节的学习,这其实也不难,不过是反复调用add或add_child而已。这里还需要注意的是property_tree采用的依然是stl的值语义,因此只有在子节点都设置好了之后再加入父节点才能保证数据不丢失。

void write_xml_demo()
{
	ptree root;

	ptree file_node;
	file_node.add("<xmlattr>.title", "windows");
	file_node.add("<xmlattr>.size", "10Mb");
	root.add_child("file", file_node); 

	root.add("<xmlcomment>", "File Fisrt Comment");
	root.add("<xmlcomment>", "File Second Comment"); 

	{
		ptree paths_node;
		paths_node.add("<xmlattr>.attr", "directory");
		paths_node.add("<xmlcomment>", "Paths Comment"); 

		ptree path_node;
		path_node.add("<xmlattr>.title", "北京");
		path_node.put_value("abc");
		paths_node.add_child("path", path_node);  

		path_node = ptree();
		path_node.add("<xmlattr>.title", "上海");
		path_node.put_value("efg");
		paths_node.add_child("path", path_node);  

		path_node = ptree();
		path_node.add("<xmlattr>.title", "广州");
		path_node.put_value("hij");
		paths_node.add_child("path", path_node);  

		root.add_child("paths", paths_node);
	}

	{
		ptree paths_node;

		ptree path_node;
		path_node.add("<xmlattr>.title", "111");
		path_node.put_value("klm");
		paths_node.add_child("path", path_node);  

		path_node = ptree();
		path_node.add("<xmlattr>.title", "222");
		path_node.put_value("nop");
		paths_node.add_child("path", path_node);  

		path_node = ptree();
		path_node.add("<xmlattr>.title", "333");
		path_node.put_value("qrs");
		paths_node.add_child("path", path_node);  

		root.add_child("paths", paths_node);
	}

	ptree doc;
	doc.add_child("config", root);

	try
	{
		xml_writer_settings<string> settings('\t', 1, "GB2312");
		write_xml("config/config.xml", doc, std::locale(), settings);
	}
	catch (std::exception& e)
	{
		cout << e.what() << endl;
	}
}

代码比较简单,不解释了。其它几种格式的文件也类似。唯一需要注意的是因为ini文件无法支持超过2层的树形结构,注意一下树形结构的层数就是了。

1.7 UNICODE和UTF-8

以上的示例代码通通都是string和GB2312的,那么property_tree是否支持UNICODE和UTF-8呢?我第一次遇到这个问题是在解析SVG文件的时候,几乎收集上来的所有SVG文件都是UTF-8的,而且我们的开发环境又必须支持UNICODE。因为read_xml和write_xml两个方法中都有locale的参数,因此这个需求是很容易实现,唯一不爽的是boost的UTF-8转换需要引入utf8_codecvt_facet.hpp,而这又是必须编译成库才能使用组件。我知道这有些偏执了,尽管大多数boost组件不需要编译成库,但是我们永远也不能保证我们就一定不会用那些需要编译成库的组件。那么好吧,编译就编译吧。

#include <iostream>
#include <boost/lexical_cast.hpp>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

#ifndef _UNICODE
#define tptree boost::property_tree::ptree
#else
#define tptree boost::property_tree::wptree
#endif 

#define BOOST_ALL_DYN_LINK
#include <boost/program_options/detail/convert.hpp>
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>

using namespace std;
using namespace boost;
using namespace ws;

using namespace boost::property_tree;

void load_svg(tptree& doc, const string& path)
{
	try
	{
		// 其中new的facet会被locale自动delete
		locale current_locale(locale(""), new program_options::detail::utf8_codecvt_facet());
		read_xml(path, doc, xml_parser::trim_whitespace, current_locale);
	}
	catch (std::exception& e)
	{
		cout << e.what() << endl;
	}
}

void save_svg(tptree& doc, const string& path)
{
	try
	{
		// 其中new的facet会被locale自动delete
		locale current_locale(locale(""), new program_options::detail::utf8_codecvt_facet());
		xml_parser::xml_writer_settings<wstring> settings(_T('\t'), 1, _T("utf-8"));
		write_xml(path, doc, current_locale, settings);
	}
	catch (std::exception& e)
	{
		cout << e.what() << endl;
	}
}

代码简单得几乎不需要解释,正好做一个轻松的结尾吧。如果以后再遇到property_tree中值得写成文字的内容,我会另开一篇文章。

时间: 2024-10-18 02:29:28

boost的property_tree组件的相关文章

Boost程序库完全开发指南——深入C++“准”标准库(第3版)

内容简介  · · · · · · Boost 是一个功能强大.构造精巧.跨平台.开源并且完全免费的C++程序库,有着“C++‘准’标准库”的美誉. Boost 由C++标准委员会部分成员所设立的Boost 社区开发并维护,使用了许多现代C++编程技术,内容涵盖字符串处理.正则表达式.容器与数据结构.并发编程.函数式编程.泛型编程.设计模式实现等许多领域,极大地丰富了C++的功能和表现力,能够使C++软件开发更加简捷.优雅.灵活和高效. <Boost程序库完全开发指南——深入C++“准”标准库(

(二)boost库之字符串格式化

(二)boost库之字符串格式化 程序中经常需要用到字符串格式化,就个人而言还是比较倾向于C格式的输出,如果只是打印日志,printf就够了,如果到生成字符串,获取你可以选择sprintf,但这些都是需要你预先分配空间的,对于一些不可预知长度的字符串格式化,就比较鸡肋了,不过还是可以实现的,如: void XString::format(const char *strFmt, ...) { va_list vl; va_start(vl, strFmt); int count = _vscpri

谈谈Boost网络编程(1)——旧系统的问题

前段时间一气呵成,把公司的陈旧代码完全替换掉了.这其间主要用到了Boost Asio,以及其他Boost库的组件(thread,bind等).这次开发,让我收获颇多. 首先,是技术上的成长.刚入公司时,负责维护的是很陈旧的代码,不过由于当时自己的视野局限,并没有认为其到底有多陈旧.后期随着技术的成长,以及视野的开阔,便有了重构系统的决定.既然说到陈旧,那么这里就简单说一下旧系统的旧在何处: 1)多线程多连接的服务端. 2)多线程多连接的客户端.第1)点和第2)点的缺点是显而易见的,线程和连接绑定

Mac下boost的安装与使用 Install and use boost library on Mac

Boost库的介绍:http://www.boost.org/ 要想在mac上使用boost库写应用程序,首先需要安装boost. 安装步骤:参考官网教程http://www.boost.org/doc/libs/1_57_0/more/getting_started/unix-variants.html 1.下载 Download boost_1_57_0.tar.bz2. 2.解压 控制台操作命名:tar --bzip2 -xf /path/to/boost_1_57_0.tar.bz2 也

Boost入门

[转载网友转载的 不过不知道原作者地址] Boost入门向导 简介:boost是一套开源的.高度可移植的C++模板库.它由C++标准委员发起,且里面很多组件有望成为下一代的C++标准库,其地位将会与STL一样.boost库的英文站点是http://www.boost.org.如果上个页面不能访问,可以看http://boost.c-view.org,它是Boost镜像.boost按功能分为:字符串.容器.算法.迭代器.数据结构.内存管理.多线程.IO等.其中字符串组中的正规表达式可以与POSIX

Linux C++学习之路(转自网络)

Module01 - Linux系统基础 由于本系列课程基于Linux(或UNIX),熟悉Linux操作系统是必要的前提. 该模块的课程包含以下方面的内容: 常用Unix/Linux命令    熟悉文件管理.文本处理.进程管理.网络.系统管理等各个方面大约100个常用的命令.    深入了解bash    了解Linux默认shell: bash 的语法.命令执行.I/O重定向.任务控制等.    正则表达式基础    由于UNIX/Linux中很多强大的文本处理命令如:grep.awk.sed

Percona XtraDB Cluster 5.6安装配置

PXC简介   Percona XtraDB Cluster(简称PXC集群)提供了MySQL高可用的一种实现方法. 1.集群是有节点组成的,推荐配置至少3个节点,但是也可以运行在2个节点上. 2.每个节点都是普通的mysql/percona服务器,可以将现有的数据库服务器组成集群,反之,也可以将集群拆分成单独的服务器. 3.每个节点都包含完整的数据副本.    PXC集群主要由两部分组成:Percona Server with XtraDB和Write Set Replication patc

linux 网络编程需要学习的内容

Linux C++培训发 课程模块 Linux C++全科班课程由以下模块组成: Module01 - Linux系统基础 由于本系列课程基于Linux(或UNIX),熟悉Linux操作系统是必要的前提. 该模块的课程包含以下方面的内容: 常用Unix/Linux命令熟悉文件管理.文本处理.进程管理.网络.系统管理等各个方面大约100个常用的命令. 深入了解bash了解Linux默认shell: bash 的语法.命令执行.I/O重定向.任务控制等. 正则表达式基础由于UNIX/Linux中很多

原子类型

http://book.51cto.com/art/201205/336658.htm 10.1.2  原子类型 前面我们看到,对于单线程上下文来说,我们可以对整型值简单地使用--和++.但是对于多线程,我们需要使用操作系统/架构原语.这种方式的缺点是即使我们将差异性抽象到一个公共的函数,例如integer_increment中,我们也要时时记住对某个整型值的所有原子操作都必须以该公共函数来完成.然而忘记其中之一是很容易的事情,一旦出现了这种情况,你就可能在应用程序中遭遇一个竞争条件,并且这种东