【Ini格式文件】Ini格式文件的读写

2023-11-13

前言

在实际工作中,常常需要读写ini格式的文件。在Gitee上找到一份开源的代码(MIT证书),其使用了文件读写的标准库函数,但我当前的嵌入式环境对文件读写的标准库函数的支持存在问题,当调用文件读写的标准库函数时会导致系统宕机。为了解决这问题,我在这一份开源代码的基础上,实现处理传入文件buffer的功能,现将代码附上。

代码

xini_file.h

/**
 * The MIT License (MIT)
 * Copyright (c) 2019-2020, Gaaagaa All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

 /**
  * @file xini_file.h
  * Copyright (c) 2019-2020, Gaaagaa All rights reserved.
  *
  * @author  :Gaaagaa
  * @date    : 2021-01-09
  * @version : 1.2.0.1
  * @brief   : `this->operator __base_type()`, gcc not supported.
  * @note    : Sepecial thanks for (qinwanlin)[https://gitee.com/qinwanlin].
  *
  * @author  :Gaaagaa
  * @date    : 2020-11-07
  * @version : 1.2.0.0
  * @brief   : Improved retrieval performance of the operator[].
  *
  * @author  :Gaaagaa
  * @date    : 2020-10-28
  * @version : 1.1.0.0
  * @brief   : update load()/release(), add operator()/try_value().
  *
  * @author  :Gaaagaa
  * @date    : 2019-11-26
  * @version : 1.0.0.0
  * @brief   : ini file parser, read and write is supported.
  */

#ifndef __XINI_FILE_H__
#define __XINI_FILE_H__

#include <stdlib.h>
#include <list>
#include <map>
#include <string>
#include <sstream>
#include <fstream>
#include <cassert>
#include <vector>

  

#if __cplusplus < 201103L
#define nullptr  0
#endif // __cplusplus < 201103L

/

/** 空白字符集 */
static const char xspace_chars[] = " \t\n\r\f\v";

/**********************************************************/
/**
 * @brief 削除字符串头尾的字符集。
 */
static inline std::string&
xstr_trim(std::string& xstr,
    const char* xtrim_chars = xspace_chars)
{
    xstr.erase(xstr.find_last_not_of(xtrim_chars) + 1);
    xstr.erase(0, xstr.find_first_not_of(xtrim_chars));

    return xstr;
}

/**********************************************************/
/**
 * @brief 削除字符串头部的字符集。
 */
static inline std::string&
xstr_ltrim(std::string& xstr,
    const char* xtrim_chars = xspace_chars)
{
    xstr.erase(0, xstr.find_first_not_of(xtrim_chars));
    return xstr;
}

/**********************************************************/
/**
 * @brief 削除字符串尾部的字符集。
 */
static inline std::string&
xstr_rtrim(std::string& xstr,
    const char* xtrim_chars = xspace_chars)
{
    xstr.erase(xstr.find_last_not_of(xtrim_chars) + 1);
    return xstr;
}

/**********************************************************/
/**
 * @brief 判断是否为单行字符串。
 */
static inline bool xstr_is_single_line(const std::string& xstr)
{
    return (xstr.find_first_of("\r\n") == std::string::npos);
}

#if 0

/**********************************************************/
/**
* @brief 字符串的比对操作。
*
* @param [in ] xszt_lcmp : 比较操作的左值字符串。
* @param [in ] xszt_rcmp : 比较操作的右值字符串。
*
* @return int
*         - xszt_lcmp <  xszt_rcmp,返回 <= -1;
*         - xszt_lcmp == xszt_rcmp,返回 ==  0;
*         - xszt_lcmp >  xszt_rcmp,返回 >=  1;
*/
static int xstr_cmp(const char* xszt_lcmp, const char* xszt_rcmp)
{
    int xit_lvalue = 0;
    int xit_rvalue = 0;

    if (xszt_lcmp == xszt_rcmp)
        return 0;
    if (NULL == xszt_lcmp)
        return -1;
    if (NULL == xszt_rcmp)
        return 1;

    do
    {
        xit_lvalue = (char)(*(xszt_lcmp++));
        xit_rvalue = (char)(*(xszt_rcmp++));
    } while (xit_lvalue && (xit_lvalue == xit_rvalue));

    return (xit_lvalue - xit_rvalue);
}

#endif

/**********************************************************/
/**
 * @brief 字符串忽略大小写的比对操作。
 *
 * @param [in ] xszt_lcmp : 比较操作的左值字符串。
 * @param [in ] xszt_rcmp : 比较操作的右值字符串。
 *
 * @return int
 *         - xszt_lcmp <  xszt_rcmp,返回 <= -1;
 *         - xszt_lcmp == xszt_rcmp,返回 ==  0;
 *         - xszt_lcmp >  xszt_rcmp,返回 >=  1;
 */
static int xstr_icmp(const char* xszt_lcmp, const char* xszt_rcmp)
{
    int xit_lvalue = 0;
    int xit_rvalue = 0;

    if (xszt_lcmp == xszt_rcmp)
        return 0;
    if (NULL == xszt_lcmp)
        return -1;
    if (NULL == xszt_rcmp)
        return 1;

    do
    {
        if (((xit_lvalue = (*(xszt_lcmp++))) >= 'A') && (xit_lvalue <= 'Z'))
            xit_lvalue -= ('A' - 'a');

        if (((xit_rvalue = (*(xszt_rcmp++))) >= 'A') && (xit_rvalue <= 'Z'))
            xit_rvalue -= ('A' - 'a');

    } while (xit_lvalue && (xit_lvalue == xit_rvalue));

    return (xit_lvalue - xit_rvalue);
}

/**
 * @struct xstr_icmp_t
 * @brief  as functor.
 */
struct xstr_icmp_t
{
    typedef std::string first_argument_type;
    typedef std::string second_argument_type;
    typedef bool        result_type;

    bool operator () (
        const std::string& xstr_left,
        const std::string& xstr_right) const
    {
        return (xstr_icmp(xstr_left.c_str(), xstr_right.c_str()) < 0);
    }
};


// xini_node_t : INI 节点的抽象定义

/**
 * <pre>
 * INI 文件格式的结构如下:
 * [文件根]
 *     |--[空行]
 *     |--[注释]
 *     +--[分节]
 *         |--[空行]
 *         |--[注释]
 *         |--[键值]
 *         |--[键值]
 *         |--[空行]
 *         |--[空行]
 *         |--[...]
 *     |--[空行]
 *     |--[空行]
 *     |--[空行]
 *     +--[分节]
 *           |--[空行]
 *           |--[注释]
 *           |--[键值]
 *           |--[空行]
 *           |--[键值]
 *           |--[键值]
 *           |--[键值]
 *           |--[空行]
 *           |--[...]
 *     |--[空行]
 *     |--[...]
 *
 * 文件根:INI 文件的虚拟名称,不存在于文件内容中。
 * 空行:空白行,即便有空白字符占据,也算空白行。
 * 注释:以 “;” 或者 “#” 开头后的内容,都算是注释内容。
 * 分节:格式为 “[section]” 。
 * 键值:格式为 “key=value” 。
 * </pre>
 */

 /**
  * @enum  xini_node_type_t
  * @brief INI 文件中的节点信息类型。
  */
typedef enum xini_ntype_t
{
    XINI_NTYPE_UNDEFINE = 0xFFFFFFFF, ///< 未定义
    XINI_NTYPE_FILEROOT = 0x00000000, ///< 文件根
    XINI_NTYPE_NILLINE = 0x00000100, ///< 空行
    XINI_NTYPE_COMMENT = 0x00000200, ///< 注释
    XINI_NTYPE_SECTION = 0x00000300, ///< 分节
    XINI_NTYPE_KEYVALUE = 0x00000400, ///< 键值
} xini_ntype_t;

/** 前置声明相关的 INI 节点类 */
class xini_keyvalue_t;
class xini_section_t;
class xini_comment_t;
class xini_nilline_t;
class xini_file_t;

/**
 * @class xini_node_t
 * @brief INI 节点描述基类。
 */
class xini_node_t
{
    friend class xini_file_t;
    friend class xini_section_t;

    // constructor/destructor
protected:
    xini_node_t(int xini_ntype, xini_node_t* xowner_ptr)
        : m_xini_ntype(xini_ntype)
        , m_xowner_ptr(xowner_ptr)
    {

    }

    virtual ~xini_node_t(void)
    {

    }

    // extensible interfaces
public:
    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流,派生的子类中必须实现具体操作。
     */
    virtual const xini_node_t& operator >> (std::ostream& ostr) const = 0;

    /**
     * @brief 将 节点信息 导向 输出流,派生的子类中必须实现具体操作。
     */
    virtual const xini_node_t& operator >> (std::string& ostr) const = 0;

    /**********************************************************/
    /**
     * @brief 脏标识。
     */
    virtual bool is_dirty(void) const
    {
        if (nullptr != m_xowner_ptr)
        {
            return m_xowner_ptr->is_dirty();
        }
        return false;
    }

    /**********************************************************/
    /**
     * @brief 设置脏标识。
     */
    virtual void set_dirty(bool x_dirty)
    {
        if (nullptr != m_xowner_ptr)
        {
            m_xowner_ptr->set_dirty(x_dirty);
        }
    }

    // public interfaces
public:
    /**********************************************************/
    /**
     * @brief 节点类型。
     */
    inline int ntype(void) const { return m_xini_ntype; }

    /**********************************************************/
    /**
     * @brief 获取节点的持有者。
     */
    inline xini_node_t* get_owner(void) const { return m_xowner_ptr; }

    // data members
protected:
    int           m_xini_ntype;   ///< 节点类型
    xini_node_t* m_xowner_ptr;   ///< 节点持有者
};

/**********************************************************/
/**
 * @brief 定义 xini_node_t 的流输出操作符函数。
 */
inline std::ostream& operator << (
    std::ostream& ostr, const xini_node_t& xini_node)
{
    xini_node >> ostr;
    return ostr;
}


// xini_nilline_t

/**
 * @class xini_nilline_t
 * @brief INI 文件中的空行节点类。
 */
class xini_nilline_t : public xini_node_t
{
    friend class xini_file_t;

    // common invoking
protected:
    /**********************************************************/
    /**
     * @brief
     * 判断已经消除头尾空白字符的字符串是否
     * 符合 xini_nilline_t 定义的格式。
     */
    static bool is_ntype(const std::string& xstr_trim_line)
    {
        return xstr_trim_line.empty();
    }

    /**********************************************************/
    /**
     * @brief 尝试使用字符串直接创建并初始化 xini_nilline_t 对象。
     */
    static xini_node_t* try_create(const std::string& xstr_trim_line,
        xini_node_t* xowner_ptr)
    {
        if (!is_ntype(xstr_trim_line))
        {
            return nullptr;
        }

        return (new xini_nilline_t(xowner_ptr));
    }

    // construcor/destructor
protected:
    xini_nilline_t(xini_node_t* xowner_ptr)
        : xini_node_t(XINI_NTYPE_NILLINE, xowner_ptr)
    {

    }

    virtual ~xini_nilline_t(void)
    {

    }

    // overrides
public:
    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流。
     */
    virtual const xini_node_t& operator >> (std::ostream& ostr) const
    {
        ostr << std::endl;
        return *this;
    }

    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流。
     */
    virtual const xini_node_t& operator >> (std::string& ostr) const
    {
        ostr += '\n';
        return *this;
    }
};


// xini_comment_t

/**
 * @class xini_comment_t
 * @brief INI 文件中的 注释 节点类。
 */
class xini_comment_t : public xini_node_t
{
    friend class xini_file_t;

    // common invoking
protected:
    /**********************************************************/
    /**
     * @brief
     * 判断已经消除头尾空白字符的字符串是否
     * 符合 xini_comment_t 定义的格式。
     */
    static bool is_ntype(const std::string& xstr_trim_line)
    {
        return (!xstr_trim_line.empty() &&
            ((';' == xstr_trim_line.at(0)) ||
                ('#' == xstr_trim_line.at(0))));
    }

    /**********************************************************/
    /**
     * @brief 尝试使用字符串直接创建并初始化 xini_comment_t 对象。
     */
    static xini_node_t* try_create(const std::string& xstr_trim_line,
        xini_node_t* xowner_ptr)
    {
        if (!is_ntype(xstr_trim_line))
        {
            return nullptr;
        }

        xini_comment_t* xnode_ptr = new xini_comment_t(xowner_ptr);
        xnode_ptr->m_xstr_text = xstr_trim_line;
        return xnode_ptr;
    }

    // construcor/destructor
protected:
    xini_comment_t(xini_node_t* xowner_ptr)
        : xini_node_t(XINI_NTYPE_COMMENT, xowner_ptr)
    {

    }

    virtual ~xini_comment_t(void)
    {

    }

    // overrides
public:
    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流。
     */
    virtual const xini_node_t& operator >> (std::ostream& ostr) const
    {
        ostr << m_xstr_text << std::endl;
        return *this;
    }

    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流。
     */
    virtual const xini_node_t& operator >> (std::string& ostr) const
    {
        ostr += m_xstr_text;
        ostr += '\n';

        return *this;
    }

protected:
    std::string   m_xstr_text;  ///< 注释行字符串
};


// xini_keyvalue_t

/**
 * @class xini_keyvalue_t
 * @brief INI 文件中的 分节 节点类。
 */
class xini_keyvalue_t : public xini_node_t
{
    friend class xini_file_t;
    friend class xini_section_t;

    // common invoking
protected:
    /**********************************************************/
    /**
     * @brief
     * 判断已经消除头尾空白字符的字符串是否
     * 符合 xini_keyvalue_t 定义的格式。
     */
    static bool is_ntype(const std::string& xstr_trim_line)
    {
        if (xstr_trim_line.empty())
        {
            return false;
        }

        // 等号位置
        size_t st_eq = xstr_trim_line.find('=');
        if ((0 == st_eq) || (std::string::npos == st_eq))
        {
            return false;
        }

        return false;
    }

    /**********************************************************/
    /**
     * @brief 尝试使用字符串直接创建并初始化 xini_keyvalue_t 对象。
     */
    static xini_node_t* try_create(const std::string& xstr_trim_line,
        xini_node_t* xowner_ptr)
    {
        if (xstr_trim_line.empty())
        {
            return nullptr;
        }

        // 等号位置
        size_t st_eq = xstr_trim_line.find('=');
        if ((0 == st_eq) || (std::string::npos == st_eq))
        {
            return nullptr;
        }

        xini_keyvalue_t* xnode_ptr = new xini_keyvalue_t(xowner_ptr);

        xnode_ptr->m_xstr_kname = xstr_trim_line.substr(0, st_eq);
        xnode_ptr->m_xstr_value = xstr_trim_line.substr(st_eq + 1);

        xstr_trim(xnode_ptr->m_xstr_kname);
        xstr_trim(xnode_ptr->m_xstr_value);

        return xnode_ptr;
    }

    // construcor/destructor
protected:
    xini_keyvalue_t(xini_node_t* xowner_ptr)
        : xini_node_t(XINI_NTYPE_KEYVALUE, xowner_ptr)
    {

    }

    virtual ~xini_keyvalue_t(void)
    {

    }

    // overrides
public:
    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流。
     */
    virtual const xini_node_t& operator >> (std::ostream& ostr) const
    {
        ostr << m_xstr_kname
            << '='
            << m_xstr_value
            << std::endl;
        return *this;
    }

    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流。
     */
    virtual const xini_node_t& operator >> (std::string& ostr) const
    {
        ostr += m_xstr_kname;
        ostr += '=';
        ostr += m_xstr_value;
        ostr += '\n';

        return *this;
    }

    // template<> functions, for operators
protected:
    /**********************************************************/
    /**
     * @brief 整数值的读操作。
     */
    template< typename __integer_type >
    __integer_type get_ivalue() const
    {
#if __cplusplus < 201103L
        return static_cast<__integer_type>(atol(m_xstr_value.c_str()));
#else // __cplusplus >= 201103L
        // atoll() 隶属于 C11 标准
        return static_cast<__integer_type>(atoll(m_xstr_value.c_str()));
#endif // __cplusplus < 201103L
    }

    /**********************************************************/
    /**
     * @brief 实现带默认值的读操作。
     */
    template< typename __base_type >
    __base_type get_default(__base_type x_default) const
    {
        if (empty())
            return x_default;
        return (__base_type)(*this);
    }

    /**********************************************************/
    /**
     * @brief 整数值的写操作。
     */
    template< typename __integer_type >
    xini_keyvalue_t& set_ivalue(__integer_type x_value)
    {
        std::ostringstream ostr;
        ostr << x_value;
        invk_set_value(ostr.str());
        return *this;
    }

    /**********************************************************/
    /**
     * @brief 实现浮点值的写操作。
     */
    template< typename __float_type >
    xini_keyvalue_t& set_fvalue(
        __float_type x_value, std::streamsize x_precision)
    {
        std::ostringstream ostr;
        ostr.precision(x_precision);
        ostr << x_value;
        invk_set_value(ostr.str());
        return *this;
    }

    /**********************************************************/
    /**
     * @brief 若当前值为 空 时,则更新为指定的值,最后再返回键值。
     */
    template< typename __base_type >
    __base_type try_set(__base_type x_value)
    {
        if (empty())
        {
            this->operator = (x_value);
        }

        return (__base_type)(*this);
    }

    // operators
public:
    //======================================
    // 基础数据类型的读操作

    operator const std::string& () const { return m_xstr_value; }
    operator const char* () const { return m_xstr_value.c_str(); }

    operator bool() const
    {
        if (0 == xstr_icmp(m_xstr_value.c_str(), "true"))
            return true;
        if (0 == xstr_icmp(m_xstr_value.c_str(), "false"))
            return false;
        return (0 != this->operator int());
    }

    operator short() const { return get_ivalue< short              >(); }
    operator unsigned short() const { return get_ivalue< unsigned short     >(); }
    operator int() const { return get_ivalue< int                >(); }
    operator unsigned int() const { return get_ivalue< unsigned int       >(); }
    operator long() const { return get_ivalue< long               >(); }
    operator unsigned long() const { return get_ivalue< unsigned long      >(); }
    operator long long() const { return get_ivalue< long long          >(); }
    operator unsigned long long() const { return get_ivalue< unsigned long long >(); }

    operator float() const { return static_cast<float>(this->operator double()); }
    operator double() const { return atof(m_xstr_value.c_str()); }

    //======================================
    // 重载 operator (),实现带上默认值的读操作

    const std::string& operator () (const std::string& x_default) const { return get_default< const std::string& >(x_default); }
    const char* operator () (const char* x_default) const { return get_default< const char*        >(x_default); }
    bool                operator () (bool                x_default) const { return get_default< bool                >(x_default); }
    short               operator () (short               x_default) const { return get_default< short               >(x_default); }
    unsigned short      operator () (unsigned short      x_default) const { return get_default< unsigned short      >(x_default); }
    int                 operator () (int                 x_default) const { return get_default< int                 >(x_default); }
    unsigned int        operator () (unsigned int        x_default) const { return get_default< unsigned int        >(x_default); }
    long                operator () (long                x_default) const { return get_default< long                >(x_default); }
    unsigned long       operator () (unsigned long       x_default) const { return get_default< unsigned long       >(x_default); }
    long long           operator () (long long           x_default) const { return get_default< long long           >(x_default); }
    unsigned long long  operator () (unsigned long long  x_default) const { return get_default< unsigned long long  >(x_default); }
    float               operator () (float               x_default) const { return get_default< float               >(x_default); }
    double              operator () (double              x_default) const { return get_default< double              >(x_default); }

    //======================================
    // 与重载的 operator () 操作符功能类似,
    // 但会使用默认值更新空键值

    const std::string& try_value(const std::string& x_default) { return try_set< const std::string& >(x_default); }
    const char* try_value(const char* x_default) { return try_set< const char*        >(x_default); }
    bool                try_value(bool                x_default) { return try_set< bool                >(x_default); }
    short               try_value(short               x_default) { return try_set< short               >(x_default); }
    unsigned short      try_value(unsigned short      x_default) { return try_set< unsigned short      >(x_default); }
    int                 try_value(int                 x_default) { return try_set< int                 >(x_default); }
    unsigned int        try_value(unsigned int        x_default) { return try_set< unsigned int        >(x_default); }
    long                try_value(long                x_default) { return try_set< long                >(x_default); }
    unsigned long       try_value(unsigned long       x_default) { return try_set< unsigned long       >(x_default); }
    long long           try_value(long long           x_default) { return try_set< long long           >(x_default); }
    unsigned long long  try_value(unsigned long long  x_default) { return try_set< unsigned long long  >(x_default); }
    float               try_value(float               x_default) { return try_set< float               >(x_default); }
    double              try_value(double              x_default) { return try_set< double              >(x_default); }

    //======================================
    // 基础数据类型的写操作

    xini_keyvalue_t& operator = (const std::string& x_value) { set_value(x_value); return *this; }
    xini_keyvalue_t& operator = (const char* x_value) { set_value(x_value); return *this; }
    xini_keyvalue_t& operator = (bool x_value) { invk_set_value(x_value ? "true" : "false"); return *this; }
    xini_keyvalue_t& operator = (short              x_value) { return set_ivalue< short              >(x_value); }
    xini_keyvalue_t& operator = (unsigned short     x_value) { return set_ivalue< unsigned short     >(x_value); }
    xini_keyvalue_t& operator = (int                x_value) { return set_ivalue< int                >(x_value); }
    xini_keyvalue_t& operator = (unsigned int       x_value) { return set_ivalue< unsigned int       >(x_value); }
    xini_keyvalue_t& operator = (long               x_value) { return set_ivalue< long               >(x_value); }
    xini_keyvalue_t& operator = (unsigned long      x_value) { return set_ivalue< unsigned long      >(x_value); }
    xini_keyvalue_t& operator = (long long          x_value) { return set_ivalue< long long          >(x_value); }
    xini_keyvalue_t& operator = (unsigned long long x_value) { return set_ivalue< unsigned long long >(x_value); }
    xini_keyvalue_t& operator = (float  x_value) { return set_fvalue(x_value, 6); }
    xini_keyvalue_t& operator = (double x_value) { return set_fvalue(x_value, 16); }

    //======================================

    // public interfaces
public:
    /**********************************************************/
    /**
     * @brief 键名。
     */
    inline const std::string& xkey(void) const
    {
        return m_xstr_kname;
    }

    /**********************************************************/
    /**
     * @brief 键值。
     */
    inline const std::string& xvalue(void) const
    {
        return m_xstr_value;
    }

    /**********************************************************/
    /**
     * @brief 判断 键值 是否为 空。
     */
    inline bool empty(void) const
    {
        return m_xstr_value.empty();
    }

    /**********************************************************/
    /**
     * @brief 设置键值。
     */
    inline void set_value(const std::string& x_value)
    {
        std::string xstr = x_value.substr(0, x_value.find_first_of("\r\n"));
        invk_set_value(xstr_trim(xstr));
    }

    // inner invoking
protected:
    /**********************************************************/
    /**
     * @brief 设置(单行文本 且 去除头尾空白字符 的)键值。
     */
    inline void invk_set_value(const std::string& xstr_single_line)
    {
        if (xstr_single_line != m_xstr_value)
        {
            m_xstr_value = xstr_single_line;
            set_dirty(true);
        }
    }

protected:
    std::string   m_xstr_kname;  ///< 键名
    std::string   m_xstr_value;  ///< 键值
};


// xini_section_t

/**
 * @class xini_section_t
 * @brief INI 文件中的 分节 节点类。
 */
class xini_section_t : public xini_node_t
{
    friend class xini_file_t;

    // common data types
protected:
    typedef std::list< xini_node_t* >                              xlst_node_t;
    typedef std::map< std::string, xini_keyvalue_t*, xstr_icmp_t > xmap_ndkv_t;

    // common invoking
protected:
    /**********************************************************/
    /**
     * @brief
     * 判断已经消除头尾空白字符的字符串是否
     * 符合 xini_section_t 定义的格式。
     */
    static bool is_ntype(const std::string& xstr_trim_line)
    {
        return (!xstr_trim_line.empty() &&
            ('[' == xstr_trim_line.at(0)) &&
            (']' == xstr_trim_line.at(xstr_trim_line.size() - 1)));
    }

    /**********************************************************/
    /**
     * @brief 尝试使用字符串直接创建并初始化 xini_section_t 对象。
     */
    static xini_node_t* try_create(const std::string& xstr_trim_line,
        xini_node_t* xowner_ptr)
    {
        if (!is_ntype(xstr_trim_line))
        {
            return nullptr;
        }

        xini_section_t* xnode_ptr = new xini_section_t(xowner_ptr);
        xnode_ptr->m_xstr_name = xstr_trim_line;

        xstr_rtrim(xnode_ptr->m_xstr_name, "]");
        xstr_ltrim(xnode_ptr->m_xstr_name, "[");
        xstr_trim(xnode_ptr->m_xstr_name);

        // 将 自身 作为 节点 加入到 m_xlst_node 中,但并不意味着 m_xlst_node 
        // 的 首个节点 就一定是 自身节点,因为 xini_file_t 在加载过程中,
        // 会调用 pop_tail_comment() 操作,这有可能在 m_xlst_node 前端新增
        // 一些 注释/空行节点。所以在进行 流输出 操作时,自身节点 则可起到 占位行
        // 的作用,详细过程可参看 operator >> 的实现流程
        xnode_ptr->m_xlst_node.push_back(xnode_ptr);

        return xnode_ptr;
    }

    // construcor/destructor
protected:
    xini_section_t(xini_node_t* xowner_ptr)
        : xini_node_t(XINI_NTYPE_SECTION, xowner_ptr)
    {

    }

    virtual ~xini_section_t(void)
    {
        for (std::list< xini_node_t* >::iterator
            itlst = m_xlst_node.begin();
            itlst != m_xlst_node.end();
            ++itlst)
        {
            if (XINI_NTYPE_SECTION != (*itlst)->ntype())
            {
                delete (*itlst);
            }
        }

        m_xlst_node.clear();
        m_xmap_ndkv.clear();
    }

    // overrides
public:
    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流。
     */
    virtual const xini_node_t& operator >> (std::ostream& ostr) const
    {
        for (std::list< xini_node_t* >::const_iterator
            itlst = m_xlst_node.begin();
            itlst != m_xlst_node.end();
            ++itlst)
        {
            if (this == static_cast<xini_section_t*>(
                const_cast<xini_node_t*>(*itlst)))
            {
                if (!m_xstr_name.empty())
                {
                    ostr << "[" << m_xstr_name << "]" << std::endl;
                }
            }
            else
            {
                **itlst >> ostr;
            }
        }

        return *this;
    }

    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流。
     */
    virtual const xini_node_t& operator >> (std::string& ostr) const
    {
        for (std::list< xini_node_t* >::const_iterator
            itlst = m_xlst_node.begin();
            itlst != m_xlst_node.end();
            ++itlst)
        {
            if (this == static_cast<xini_section_t*>(
                const_cast<xini_node_t*>(*itlst)))
            {
                if (!m_xstr_name.empty())
                {
                    ostr += "[";
                    ostr += m_xstr_name;
                    ostr += "]";
                    ostr += "\n";
                }
            }
            else
            {
                **itlst >> ostr;
            }
        }

        return *this;
    }

    // overrides : operator
public:
    /**********************************************************/
    /**
     * @brief 重载 operator [] 操作符,实现 键值 节点的索引操作。
     */
    xini_keyvalue_t& operator [] (const std::string& xstr_key)
    {
        assert(xstr_is_single_line(xstr_key));

        //======================================

        std::string xstr_nkey = xstr_key;
        xstr_trim(xstr_nkey);

        //======================================

        xini_keyvalue_t* xknode_ptr = find_knode(xstr_nkey);
        if (nullptr != xknode_ptr)
        {
            return *xknode_ptr;
        }

        //======================================
        // 若索引的 键值节点 并未在节点表中,
        // 则 新增 此 键值节点,但并不设置 脏标识,
        // 避免存储不必要的 空键值节点

        xknode_ptr =
            static_cast<xini_keyvalue_t*>(
                xini_keyvalue_t::try_create(xstr_nkey + "=", get_owner()));
        assert(nullptr != xknode_ptr);

        m_xlst_node.push_back(xknode_ptr);
        m_xmap_ndkv.insert(std::make_pair(xstr_nkey, xknode_ptr));

        //======================================

        return *xknode_ptr;
    }

    // public interfaces
public:
    /**********************************************************/
    /**
     * @brief 分节 名称。
     */
    inline const std::string& name(void) const
    {
        return m_xstr_name;
    }

    /**********************************************************/
    /**
     * @brief 分节 内的节点数量。
     */
    inline size_t size(void) const
    {
        return m_xlst_node.size();
    }

    /**********************************************************/
    /**
     * @brief 分节 是否为空。
     */
    inline bool empty() const
    {
        return m_xlst_node.empty();
    }

    /**********************************************************/
    /**
     * @brief 判断当前分节是否以空行结尾。
     */
    inline bool has_end_nilline(void)
    {
        if (!m_xlst_node.empty() &&
            (XINI_NTYPE_NILLINE == m_xlst_node.back()->ntype()))
        {
            return true;
        }
        return false;
    }

    // inner invoking
protected:
    /**********************************************************/
    /**
     * @brief 添加(空行、注释、键值 类型的)节点。
     *
     * @param [in ] xnode_ptr: (空行、注释、键值 类型的)节点。
     *
     * @return bool
     *         - 成功,返回 true ;
     *         - 失败,返回 false。
     */
    bool push_node(xini_node_t* xnode_ptr)
    {
        if (nullptr == xnode_ptr)
        {
            return false;
        }

        if ((XINI_NTYPE_NILLINE == xnode_ptr->ntype()) ||
            (XINI_NTYPE_COMMENT == xnode_ptr->ntype()))
        {
            m_xlst_node.push_back(xnode_ptr);
            return true;
        }

        if (XINI_NTYPE_KEYVALUE == xnode_ptr->ntype())
        {
            xini_keyvalue_t* xnode_kvptr =
                static_cast<xini_keyvalue_t*>(xnode_ptr);

            if (nullptr != find_knode(xnode_kvptr->xkey()))
            {
                return false;
            }

            m_xlst_node.push_back(xnode_ptr);
            m_xmap_ndkv.insert(std::make_pair(xnode_kvptr->xkey(), xnode_kvptr));
            return true;
        }

        return false;
    }

    /**********************************************************/
    /**
     * @brief 查找分节下的 键值 节点。
     *
     * @param [in ] xstr_xkey: 索引键字符串,比较时忽略大小写。
     *
     * @return xini_keyvalue_t *
     *         - 成功,返回 对应的节点;
     *         - 失败,返回 nullptr 。
     */
    xini_keyvalue_t* find_knode(const std::string& xstr_xkey) const
    {
#if 0
        for (std::list< xini_node_t* >::const_iterator
            itlst = m_xlst_node.begin();
            itlst != m_xlst_node.end();
            ++itlst)
        {
            if (XINI_NTYPE_KEYVALUE != (*itlst)->ntype())
            {
                continue;
            }

            xini_keyvalue_t* xnode_ptr =
                static_cast<xini_keyvalue_t*>(
                    const_cast<xini_node_t*>(*itlst));
            if (0 == xstr_icmp(xstr_xkey.c_str(), xnode_ptr->xkey().c_str()))
            {
                return xnode_ptr;
            }
        }
#else
        xmap_ndkv_t::const_iterator itfind = m_xmap_ndkv.find(xstr_xkey);
        if (itfind != m_xmap_ndkv.end())
        {
            return itfind->second;
        }
#endif
        return nullptr;
    }

    /**********************************************************/
    /**
     * @brief 从 节点表 尾部取出 非当前 分节 下的注释节点(按 空行 节点作为分界)。
     *
     * @param [in ] xlst_comm : 接收返回的注释节点表(在链表头部添加返回的节点)。
     * @param [in ] xbt_front : 表明操作是从 xlst_comm 前/后附加返回的节点。
     *
     * @return size_t
     *         - 返回取出的节点数量。
     */
    size_t pop_tail_comment(std::list< xini_node_t* >& xlst_comm, bool xbt_front)
    {
        std::list< xini_node_t* > xlst_node;

        size_t xst_line = 0;
        size_t xst_maxl = m_xlst_node.size();

        // 节点表只有三种类型的节点:键值,空行,注释,
        // 以及 另外加上 自身的 分节节点

        while ((xst_line++ < xst_maxl) && !m_xlst_node.empty())
        {
            xini_node_t* xnode_ptr = m_xlst_node.back();

            // 遇到空行节点
            if (XINI_NTYPE_NILLINE == xnode_ptr->ntype())
            {
                if (xst_line > 1)
                    break;

                // 只容许第一个是空行
                xlst_node.push_front(xnode_ptr);
                m_xlst_node.pop_back();
                continue;
            }

            // 若反向遍历过程中,一直未遇到空行,
            // 则将原取出的注释节点还回节点表中
            if ((XINI_NTYPE_KEYVALUE == xnode_ptr->ntype()) ||
                (XINI_NTYPE_SECTION == xnode_ptr->ntype()))
            {
                m_xlst_node.splice(m_xlst_node.end(), xlst_node);
                break;
            }

            if (XINI_NTYPE_COMMENT == xnode_ptr->ntype())
            {
                xlst_node.push_front(xnode_ptr);
                m_xlst_node.pop_back();
            }
            else
            {
                // 未识别的节点类型
                assert(false);
            }
        }

        size_t xst_count = xlst_node.size();
        if (xst_count > 0)
        {
            // 设置返回结果
            if (xbt_front)
            {
                xlst_node.splice(xlst_node.end(), xlst_comm);
                xlst_comm.swap(xlst_node);
            }
            else
            {
                xlst_comm.splice(xlst_comm.end(), xlst_node);
            }
        }

        return xst_count;
    }

protected:
    std::string   m_xstr_name;  ///< 分节名称
    xlst_node_t   m_xlst_node;  ///< 分节下的节点表
    xmap_ndkv_t   m_xmap_ndkv;  ///< 分节下的 键值节点 映射表
};


// xini_file_t

/**
 * @class xini_file_t
 * @brief INI 文件操作类。
 */
class xini_file_t : public xini_node_t
{
    // common data types
protected:
    typedef std::list< xini_section_t* >                          xlst_section_t;
    typedef std::map< std::string, xini_section_t*, xstr_icmp_t > xmap_section_t;

    // common invoking
protected:
    /**********************************************************/
    /**
     * @brief 依据给定的 INI 文本行,创建相应的节点。
     */
    static xini_node_t* make_node(const std::string& xstr_line,
        xini_file_t* xowner_ptr)
    {
        xini_node_t* xnode_ptr = nullptr;

#define XTRY_CREATE(nptr, node, owner)                 \
        do                                             \
        {                                              \
            nptr = node::try_create(xstr_line, owner); \
            if (nullptr != nptr)                       \
                return nptr;                           \
        } while (0)

        XTRY_CREATE(xnode_ptr, xini_nilline_t, xowner_ptr);
        XTRY_CREATE(xnode_ptr, xini_comment_t, xowner_ptr);
        XTRY_CREATE(xnode_ptr, xini_section_t, xowner_ptr);
        XTRY_CREATE(xnode_ptr, xini_keyvalue_t, xowner_ptr);

#undef XTRY_CREATE

        return xnode_ptr;
    }

    // constructor/destructor
public:
    xini_file_t(void)
        : xini_node_t(XINI_NTYPE_FILEROOT, nullptr)
        , m_xbt_dirty(false)
    {

    }

    xini_file_t(const std::string& xstr_filepath)
        : xini_node_t(XINI_NTYPE_FILEROOT, nullptr)
        , m_xbt_dirty(false)
    {
        load(xstr_filepath);
    }

    xini_file_t(const void* data, unsigned int dataSize)
        : xini_node_t(XINI_NTYPE_FILEROOT, nullptr)
        , m_xbt_dirty(false)
    {
        load(data, dataSize);
    }

    virtual ~xini_file_t(void)
    {
        release();
    }

    // overrides
public:
    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流。
     */
    virtual const xini_node_t& operator >> (std::ostream& ostr) const
    {
        for (std::list< xini_section_t* >::const_iterator
            itlst = m_xlst_sect.begin();
            itlst != m_xlst_sect.end();
            ++itlst)
        {
            if ((*itlst)->empty())
                continue;

            **itlst >> ostr;
            if (!(*itlst)->has_end_nilline() &&
                ((*itlst) != m_xlst_sect.back()))
            {
                ostr << std::endl;
            }
        }

        return *this;
    }

    /**********************************************************/
    /**
     * @brief 将 节点信息 导向 输出流。
     */
    virtual const xini_node_t& operator >> (std::string& ostr) const
    {
        for (std::list< xini_section_t* >::const_iterator
            itlst = m_xlst_sect.begin();
            itlst != m_xlst_sect.end();
            ++itlst)
        {
            if ((*itlst)->empty())
                continue;

            **itlst >> ostr;
            if (!(*itlst)->has_end_nilline() &&
                ((*itlst) != m_xlst_sect.back()))
            {
                ostr += '\n';
            }
        }

        return *this;
    }

    /**********************************************************/
    /**
     * @brief 脏标识。
     */
    virtual bool is_dirty(void) const
    {
        return m_xbt_dirty;
    }

    /**********************************************************/
    /**
     * @brief 设置脏标识。
     */
    virtual void set_dirty(bool x_dirty)
    {
        m_xbt_dirty = x_dirty;
    }

    // overrides : operator
public:
    /**********************************************************/
    /**
     * @brief 从 输出流 构建 xini_file_t 内容。
     */
    xini_file_t& operator << (std::istream& istr)
    {
        //======================================

        // 记录当前操作的分节
        xini_section_t* xsect_ptr = nullptr;

        if (m_xlst_sect.empty())
        {
            // 当前分节表为空,则创建一个空分节名的 分节 节点
            xsect_ptr = new xini_section_t(this);
            m_xlst_sect.push_back(xsect_ptr);

            assert(m_xmap_sect.empty());
            m_xmap_sect.insert(std::make_pair(std::string(""), xsect_ptr));
        }
        else
        {
            // 取尾部分节作为当前操作的 分节 节点
            xsect_ptr = m_xlst_sect.back();

            // 确保尾部分节空行结尾
            if (!xsect_ptr->has_end_nilline())
            {
                xsect_ptr->push_node(new xini_nilline_t(this));
            }
        }

        //======================================

        // 逐行解析 INI 文件,构建节点表
        while (!istr.eof())
        {
            //======================================
            // 读取文本行

            std::string xstr_line;
            std::getline(istr, xstr_line);
            xstr_trim(xstr_line);

            // 最后一个空行不放到节点表中,避免文件关闭时 持续增加 尾部空行
            if (istr.eof() && xstr_line.empty())
            {
                break;
            }

            //======================================

            // 创建节点
            xini_node_t* xnode_ptr = make_node(xstr_line, this);
            if (nullptr == xnode_ptr)
            {
                continue;
            }

            // 若为 分节 节点,则加入到分节表中,并更新当前操作的 分节节点
            if (XINI_NTYPE_SECTION == xnode_ptr->ntype())
            {
                xsect_ptr =
                    push_sect(static_cast<xini_section_t*>(xnode_ptr),
                        xsect_ptr);

                if (xsect_ptr != static_cast<xini_section_t*>(xnode_ptr))
                    delete xnode_ptr; // 添加新分节失败,删除该节点
                else
                    set_dirty(true);  // 添加新分节成功,设置脏标识

                continue;
            }

            // 加入 当前分节
            if (xsect_ptr->push_node(xnode_ptr))
            {
                set_dirty(true);
            }
            else
            {
                // 加入分节失败,可能是因为:
                // 其为 键值 节点,与 分节 节点表中已有的 节点 索引键 冲突
                delete xnode_ptr;
            }

            //======================================
        }

        //======================================

        return *this;
    }
    /**********************************************************/
    /**
     * @brief 从 输出流 构建 xini_file_t 内容。
     */
    xini_file_t& operator << (std::string& istr)
    {
        //======================================

        // 记录当前操作的分节
        xini_section_t* xsect_ptr = nullptr;

        if (m_xlst_sect.empty())
        {
            // 当前分节表为空,则创建一个空分节名的 分节 节点
            xsect_ptr = new xini_section_t(this);
            m_xlst_sect.push_back(xsect_ptr);

            assert(m_xmap_sect.empty());
            m_xmap_sect.insert(std::make_pair(std::string(""), xsect_ptr));
        }
        else
        {
            // 取尾部分节作为当前操作的 分节 节点
            xsect_ptr = m_xlst_sect.back();

            // 确保尾部分节空行结尾
            if (!xsect_ptr->has_end_nilline())
            {
                xsect_ptr->push_node(new xini_nilline_t(this));
            }
        }

        //======================================
        
        // 逐行解析 INI 文件,构建节点表
        int idx = 0;
        while (idx <= istr.size())
        {
            //======================================
            // 读取文本行
            std::string xstr_line;
            for (; idx <= istr.size(); idx++) {
                if (istr[idx] == '\n' || istr[idx] == '\0') {
                    idx++;
                    break;
                }
                xstr_line += istr[idx];
            }
           
            xstr_trim(xstr_line);

            // 最后一个空行不放到节点表中,避免文件关闭时 持续增加 尾部空行
            if (idx == istr.size() && xstr_line.empty())
            {
                break;
            }
            
            //======================================

            // 创建节点
            xini_node_t* xnode_ptr = make_node(xstr_line, this);
            if (nullptr == xnode_ptr)
            {
                continue;
            }

            // 若为 分节 节点,则加入到分节表中,并更新当前操作的 分节节点
            if (XINI_NTYPE_SECTION == xnode_ptr->ntype())
            {
                xsect_ptr =
                    push_sect(static_cast<xini_section_t*>(xnode_ptr),
                        xsect_ptr);

                if (xsect_ptr != static_cast<xini_section_t*>(xnode_ptr))
                    delete xnode_ptr; // 添加新分节失败,删除该节点
                else
                    set_dirty(true);  // 添加新分节成功,设置脏标识

                continue;
            }

            // 加入 当前分节
            if (xsect_ptr->push_node(xnode_ptr))
            {
                set_dirty(true);
            }
            else
            {
                // 加入分节失败,可能是因为:
                // 其为 键值 节点,与 分节 节点表中已有的 节点 索引键 冲突
                delete xnode_ptr;
            }

            //======================================
        }

        //======================================

        return *this;
    }
    /**********************************************************/
    /**
     * @brief 重载 operator [] 操作符,实现 分节 索引操作。
     */
    xini_section_t& operator [] (const std::string& xstr_sect)
    {
        assert(xstr_is_single_line(xstr_sect));

        //======================================

        std::string xstr_name = xstr_sect;
        xstr_trim(xstr_name);
        xstr_rtrim(xstr_name, "]");
        xstr_ltrim(xstr_name, "[");
        xstr_trim(xstr_name);

        //======================================

        xini_section_t* xsect_ptr = find_sect(xstr_name);
        if (nullptr != xsect_ptr)
        {
            return *xsect_ptr;
        }

        //======================================
        // 若索引的分节并未在 分节 的节点表中,
        // 则 新增 此分节,但并不设置 脏标识,
        // 避免存储不必要的  空分节

        xsect_ptr =
            static_cast<xini_section_t*>(
                xini_section_t::try_create("[" + xstr_name + "]", this));
        assert(nullptr != xsect_ptr);

        m_xlst_sect.push_back(xsect_ptr);
        m_xmap_sect.insert(std::make_pair(xstr_name, xsect_ptr));

        //======================================

        return *xsect_ptr;
    }

    // public interfaces
public:
    /**********************************************************/
    /**
     * @brief 从指定路径的文件中加载 INI 内容。
     * @note
     *  load() 操作的成功与否,并不影响后续的键值读写操作,
     *  其只能标示 xini_file_t 对象是否关联可至指定路径
     *  (本地磁盘 或 远程网络 等的)文件。
     *
     * @param [in ] xstr_text : 文件路径。
     *
     * @return bool
     *         - 成功,返回 true ;
     *         - 失败,返回 false。
     */
    bool load(const std::string& xstr_filepath)
    {
        // 先释放当前对象
        release();

        // 不管后续操作是否成功,都关联到新指定的 INI 文件路径
        m_xstr_path = xstr_filepath;

        if (xstr_filepath.empty())
        {
            return false;
        }

        // 打开文件
        std::ifstream xfile_reader(xstr_filepath.c_str());
        if (!xfile_reader.is_open())
        {
            return false;
        }

        // 跳过字符流的头部编码信息(如 utf-8 的 bom 标识)
        while (!xfile_reader.eof())
        {
            int xchar = xfile_reader.get();
            if (std::iscntrl(xchar) || std::isprint(xchar))
            {
                xfile_reader.putback(static_cast<char>(xchar));
                break;
            }

            m_xstr_head.push_back(static_cast<char>(xchar));
        }

        *this << xfile_reader;
        set_dirty(false);

        return true;
    }

    /**********************************************************/
    /**
     * @brief 从输入文件流中加载 INI 内容。
     * @note
     *  load() 操作的成功与否,并不影响后续的键值读写操作,
     *  其只能标示 xini_file_t 对象是否关联可至指定路径
     *  (本地磁盘 或 远程网络 等的)文件。
     *
     * @param [in ] xstr_text : 文件路径。
     *
     * @return bool
     *         - 成功,返回 true ;
     *         - 失败,返回 false。
     */
    bool load(const void* fileData, unsigned int dataSize)
    {
        // 先释放当前对象
        release();
      
        if (nullptr == fileData || 0 == dataSize)
        {
            return false;
        }
        
        // 跳过字符流的头部编码信息(如 utf-8 的 bom 标识)
        char* data = (char*)fileData;

        int idx = 0;
        std::string xfile_content;
        unsigned int count = 0;
        while (idx <= dataSize)
        {
            int xchar = *(data + idx);

            if (std::iscntrl(xchar) || std::isprint(xchar))
            {
                xfile_content = data + idx;
                break;
            }
            m_xstr_head.push_back(static_cast<char>(xchar));
            idx++;
        }

        xfile_content += '\0';

        *this << xfile_content;
        set_dirty(false);

        return true;

    }

    /**********************************************************/
    /**
     * @brief 释放对象资源(可以不显示调用,对象析构函数中会自动调用该接口)。
     */
    void release(void)
    {
        if (is_dirty())
        {
            dump(m_xstr_path);
            set_dirty(false);
        }
        m_xstr_path.clear();
        m_xstr_head.clear();

        for (std::list< xini_section_t* >::iterator
            itlst = m_xlst_sect.begin();
            itlst != m_xlst_sect.end();
            ++itlst)
        {
            delete* itlst;
        }

        m_xlst_sect.clear();
        m_xmap_sect.clear();
    }

    /**********************************************************/
    /**
     * @brief 当前关联的文件路径。
     */
    inline const std::string& filepath(void) const
    {
        return m_xstr_path;
    }

    /**********************************************************/
    /**
     * @brief 返回当前分节数量。
     */
    inline size_t sect_count(void) const
    {
        return m_xlst_sect.size();
    }

    /**********************************************************/
    /**
     * @brief 将当前文件根下的所有节点直接输出到文件中。
     */
    bool dump(const std::string& xstr_filepath)
    {
        // 打开文件
        std::ofstream xfile_writer(
            xstr_filepath.c_str(), std::ios_base::trunc);
        if (!xfile_writer.is_open())
        {
            return false;
        }

        if (!m_xstr_head.empty())
            xfile_writer << m_xstr_head.c_str();
        *this >> xfile_writer;

        return true;
    }

    /**********************************************************/
    /**
     * @brief 将当前文件根下的所有节点直接输出到文件缓冲区中。
     */
    std::string dump()
    {
        std::string ostr;

        if (!m_xstr_head.empty()) {
            ostr += m_xstr_head;
        }
            
        *this >> ostr;

		set_dirty(false);

        return ostr;
    }

    // inner invoking
protected:
    /**********************************************************/
    /**
     * @brief 查找分节。
     */
    xini_section_t* find_sect(const std::string& xstr_sect) const
    {
#if 0
        for (std::list< xini_section_t* >::const_iterator
            itlst = m_xlst_sect.begin();
            itlst != m_xlst_sect.end();
            ++itlst)
        {
            if (0 == xstr_icmp(xstr_sect.c_str(),
                (*itlst)->name().c_str()))
            {
                return (*itlst);
            }
        }
#else
        xmap_section_t::const_iterator itfind = m_xmap_sect.find(xstr_sect);
        if (itfind != m_xmap_sect.end())
        {
            return itfind->second;
        }
#endif
        return nullptr;
    }

    /**********************************************************/
    /**
     * @brief 加入新分节(该接口仅由 operator << 调用)。
     *
     * @param [in ] xnew_ptr  : 新增分节。
     * @param [in ] xsect_ptr : 当前操作分节。
     *
     * @return xini_section_t *
     *         - 返回当前操作分节。
     *         - 若返回值 != xnew_ptr 则表示操作失败,新增分节和内部分节重名。
     */
    xini_section_t* push_sect(xini_section_t* xnew_ptr,
        xini_section_t* xsect_ptr)
    {
        // 查找同名分节
        xini_section_t* xfind_ptr = find_sect(xnew_ptr->name());

        if (nullptr == xfind_ptr)
        {
            // 不存在同名分节,则将新增分节加入到节点表尾部
            m_xlst_sect.push_back(xnew_ptr);
            m_xmap_sect.insert(std::make_pair(xnew_ptr->name(), xnew_ptr));

            // 将当前操作分节的节点表中的 尾部注释节点,
            // 全部转移到新增分节的节点表前
            xsect_ptr->pop_tail_comment(xnew_ptr->m_xlst_node, true);

            // 将新增分节作为当前操作分节返回
            xsect_ptr = xnew_ptr;
        }
        else if (xfind_ptr != xsect_ptr)
        {
            // 将当前操作分节的节点表中的 尾部注释节点,
            // 全部转移到同名分节的节点表后

            // 保证空行隔开
            if (!xfind_ptr->has_end_nilline())
            {
                xfind_ptr->push_node(new xini_nilline_t(this));
            }

            // 增加注释节点
            xsect_ptr->pop_tail_comment(xfind_ptr->m_xlst_node, false);

            // 保证空行隔开
            if (!xfind_ptr->has_end_nilline())
            {
                xfind_ptr->push_node(new xini_nilline_t(this));
            }

            // 将同名分节作为当前操作分节返回
            xsect_ptr = xfind_ptr;
        }

        return xsect_ptr;
    }

    // data members
protected:
    bool              m_xbt_dirty;  ///< 脏标识
    std::string       m_xstr_path;  ///< 文件路径
    std::string       m_xstr_head;  ///< 用于存储文件头的编码字符信息(如 utf-8 的 bom 标识)
    xlst_section_t    m_xlst_sect;  ///< 文件根下的 分节 节点表
    xmap_section_t    m_xmap_sect;  ///< 各个 分节 的节点映射表
};

/**********************************************************/
/**
 * @brief 定义 xini_file_t 的流输入操作符函数。
 */
inline std::istream& operator >> (
    std::istream& istr, xini_file_t& xini_file)
{
    xini_file << istr;
    return istr;
}



#endif // __XINI_FILE_H__

xstr_traits.h

/**
 * The MIT License (MIT)
 * Copyright (c) 2019-2020, Gaaagaa All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @file xstr_traits.h
 * Copyright (c) 2021 Gaaagaa. All rights reserved.
 * 
 * @author  : Gaaagaa
 * @date    : 2021-03-26
 * @version : 1.0.0.0
 * @brief   : 提供 xstr_traits_t 类辅助字符串的一些操作。
 */

#ifndef __XSTR_TRAITS_H__
#define __XSTR_TRAITS_H__

#if __cplusplus < 201103L
// This support must be enabled with the -std=c++11 or -std=gnu++11 compiler options.
#error "This file requires compiler and library support for the ISO C++ 2011 standard."
#endif // __cplusplus < 201103L

#include <string>
#include <iostream>

/

/**
 * @brief 声明 xstr_traits 的基类。
 * @tparam __char_t : char or wchar_t。
 */
template< class __char_t > struct xstr_traits_base_t;

/**
 * @struct xstr_traits_base_t< char >
 * @brief  特化 xstr_traits_base_t< char > 基类。
 */
struct xstr_traits_base_t< char >
{
    /** 空白字符集 */
    static constexpr const char xchar_set_space[] = " \t\n\r\f\v";

    /** 换行字符集 */
    static constexpr const char xchar_set_nline[] = "\r\n";

    /** "[" 字符集 */
    static constexpr const char xchar_set_lsect[] = "[";

    /** "]" 字符集 */
    static constexpr const char xchar_set_rsect[] = "]";

    /** '#' 字符 */
    static constexpr const char xchar_number_sign = '#';

    /** ';' 字符 */
    static constexpr const char xchar_semicolon = ';';

    /** '=' 字符 */
    static constexpr const char xchar_equal = '=';

    /** '[' 字符 */
    static constexpr const char xchar_lsect = '[';

    /** ']' 字符 */
    static constexpr const char xchar_rsect = ']';
};

/**
 * @struct xstr_traits_base_t< wchar_t >
 * @brief  特化 xstr_traits_base_t< wchar_t > 基类。
 */
struct xstr_traits_base_t< wchar_t >
{
    /** 空白字符集 */
    static constexpr const wchar_t xchar_set_space[] = L" \t\n\r\f\v";

    /** 换行字符集 */
    static constexpr const wchar_t xchar_set_nline[] = L"\r\n";

    /** "[" 字符集 */
    static constexpr const wchar_t xchar_set_lsect[] = L"[";

    /** "]" 字符集 */
    static constexpr const wchar_t xchar_set_rsect[] = L"]";

    /** '#' 字符 */
    static constexpr const wchar_t xchar_number_sign = L'#';

    /** ';' 字符 */
    static constexpr const wchar_t xchar_semicolon = L';';

    /** '=' 字符 */
    static constexpr const wchar_t xchar_equal = L'=';

    /** '[' 字符 */
    static constexpr const wchar_t xchar_lsect = L'[';

    /** ']' 字符 */
    static constexpr const wchar_t xchar_rsect = L']';
};

/**
 * @class xstr_traits_t< __char_t >
 * @brief 字符串各种操作的辅助工具类。
 * @tparam __char_t : char or wchar_t。
 */
template< class __char_t >
class xstr_traits_t : xstr_traits_base_t< __char_t >
{
    // common data types
public:
    using xchar_t = __char_t;
    using xsize_t = size_t;
    using xstring = std::basic_string< __char_t >;
    using xstrios = std::basic_ios< __char_t >;
    using xsuper  = xstr_traits_base_t< __char_t >;

    // common invoking
public:
    /**********************************************************/
    /**
     * @brief 削除字符串头尾的字符集。
     */
    static inline xstring &
        trim(xstring & xstr, const xchar_t * xtrim_chars = xsuper::xchar_set_space)
    {
        xstr.erase(xstr.find_last_not_of(xtrim_chars) + 1);
        xstr.erase(0, xstr.find_first_not_of(xtrim_chars));

        return xstr;
    }

    /**********************************************************/
    /**
     * @brief 削除字符串头部的字符集。
     */
    static inline xstring &
        ltrim(xstring & xstr, const xchar_t * xtrim_chars = xsuper::xchar_set_space)
    {
        xstr.erase(0, xstr.find_first_not_of(xtrim_chars));
        return xstr;
    }

    /**********************************************************/
    /**
     * @brief 削除字符串尾部的字符集。
     */
    static inline xstring &
        rtrim(xstring & xstr, const xchar_t * xtrim_chars = xsuper::xchar_set_space)
    {
        xstr.erase(xstr.find_last_not_of(xtrim_chars) + 1);
        return xstr;
    }

    /**********************************************************/
    /**
     * @brief 判断是否为单行字符串。
     */
    static inline bool is_single_line(const xstring & xstr)
    {
        return (xstr.find_first_of(xsuper::xchar_set_nline) == std::string::npos);
    }

    /**********************************************************/
    /**
    * @brief 字符串的比对操作。
    *
    * @param [in ] xszt_lcmp : 比较操作的左值字符串。
    * @param [in ] xszt_rcmp : 比较操作的右值字符串。
    *
    * @return int
    *         - xszt_lcmp <  xszt_rcmp,返回 <= -1;
    *         - xszt_lcmp == xszt_rcmp,返回 ==  0;
    *         - xszt_lcmp >  xszt_rcmp,返回 >=  1;
    */
    static int cmp(const xchar_t * xszt_lcmp, const xchar_t * xszt_rcmp)
    {
        int xit_lvalue = 0;
        int xit_rvalue = 0;

        if (xszt_lcmp == xszt_rcmp)
            return 0;
        if (NULL == xszt_lcmp)
            return -1;
        if (NULL == xszt_rcmp)
            return 1;

        do
        {
            xit_lvalue = *(xszt_lcmp++);
            xit_rvalue = *(xszt_rcmp++);
        } while (xit_lvalue && (xit_lvalue == xit_rvalue));

        return (xit_lvalue - xit_rvalue);
    }

    /**********************************************************/
    /**
     * @brief 字符串忽略大小写的比对操作。
     *
     * @param [in ] xszt_lcmp : 比较操作的左值字符串。
     * @param [in ] xszt_rcmp : 比较操作的右值字符串。
     *
     * @return int
     *         - xszt_lcmp <  xszt_rcmp,返回 <= -1;
     *         - xszt_lcmp == xszt_rcmp,返回 ==  0;
     *         - xszt_lcmp >  xszt_rcmp,返回 >=  1;
     */
    static int icmp(const xchar_t * xszt_lcmp, const xchar_t * xszt_rcmp)
    {
        const int xit_ivcmp = xstrios::widen('A') - xstrios::widen('a');

        int xit_lvalue = 0;
        int xit_rvalue = 0;

        if (xszt_lcmp == xszt_rcmp)
            return 0;
        if (NULL == xszt_lcmp)
            return -1;
        if (NULL == xszt_rcmp)
            return 1;

        do
        {
            if (((xit_lvalue = (*(xszt_lcmp++))) >= xstrios::widen('A')) &&
                 (xit_lvalue <= xstrios::widen('Z')))
                xit_lvalue -= xit_ivcmp;

            if (((xit_rvalue = (*(xszt_rcmp++))) >= xstrios::widen('A')) &&
                 (xit_rvalue <= xstrios::widen('Z')))
                xit_rvalue -= xit_ivcmp;
        } while (xit_lvalue && (xit_lvalue == xit_rvalue));

        return (xit_lvalue - xit_rvalue);
    }

    /**
     * @struct xstr_icmp_t
     * @brief  as functor.
     */
    struct xstr_icmp_t
    {
        typedef xstring first_argument_type;
        typedef xstring second_argument_type;
        typedef bool    result_type;

        bool operator () (const xstring & xstr_left, const xstring & xstr_right) const
        {
            return (icmp(xstr_left.c_str(), xstr_right.c_str()) < 0);
        }
    };
};



#endif // __XSTR_TRAITS_H__

ini_test.cpp

/**
 * The MIT License (MIT)
 * Copyright (c) Gaaagaa. All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @file ini_test.cpp
 * Copyright (c) 2021 Gaaagaa. All rights reserved.
 * 
 * @author  : Gaaagaa
 * @date    : 2021-03-26
 * @version : 1.0.0.0
 * @brief   : 对 xini_file_t 类的测试程序。
 */

#include "xini_file.h"

#include <iostream>
#include <stdlib.h>
#include <time.h>

#include <locale>


// 各个测试流程的函数声明

/**********************************************************/
/**
 * @brief 测试 INI 的数据读取操作(这其中也包括 带默认值 的读取操作)。
 */
void test_ini_read(const std::string & xstr_file);

/**********************************************************/
/**
 * @brief 测试 INI 的数据写入操作。
 */
void test_ini_write(const std::string & xstr_file);

/**********************************************************/
/**
 * @brief 
 * 测试 INI 键值节点的 try_value() 操作接口,
 * 以及自定义数据类型的读写操作。
 * @note 
 * 键值节点的 try_value() 实现的功能:
 * 返回当前节点值,但若节点值为 空,则设置为指定值。
 */
void test_ini_vtry(void);


// 测试程序的入口 main() 函数

int main(int argc, char * argv[])
{
    if (argc < 2)
    {
        std::cout << "usage : "
                  << argv[0]
                  << " < ini file > [ locale : zh_CN.utf8 or en_US.utf8 ... ]"
                  << std::endl;
        return -1;
    }

    std::string xstr_file = argv[1];
    if (argc >= 3)
    {
        std::cout << "locale : "
                  << setlocale(LC_ALL, argv[2])
                  << std::endl;
    }

    test_ini_read(xstr_file);
    test_ini_write(xstr_file);

    test_ini_vtry();

    return 0;
}


// 各个测试流程的函数具体实现

/**********************************************************/
/**
 * @brief 测试 INI 的数据读取操作(这其中也包括 带默认值 的读取操作)。
 */
void test_ini_read(const std::string & xstr_file)
{
    xini_file_t xini_file(xstr_file);

    std::cout.precision(20);
    std::cout.setf(std::ios_base::boolalpha);

    std::cout << "//====================================================================" << std::endl;
    std::cout << "test_ini_read() output: " << std::endl;
    std::cout << "----------------------------------------" << std::endl;

    // 常规的读取操作
    std::cout << "[section1]" << std::endl;
    std::cout << "Text1   : " << (const std::string &)xini_file["section1"]["Text1"  ] << std::endl;
    std::cout << "Text2   : " << (const char *       )xini_file["section1"]["Text2"  ] << std::endl;
    std::cout << "Text3   : " << (const char *       )xini_file["section1"]["Text3"  ] << std::endl;
    std::cout << "Bool1   : " << (bool               )xini_file["section1"]["Bool1"  ] << std::endl;
    std::cout << "Bool2   : " << (bool               )xini_file["section1"]["Bool2"  ] << std::endl;
    std::cout << "Int1    : " << (int                )xini_file["section1"]["Int1"   ] << std::endl;
    std::cout << "Int2    : " << (int                )xini_file["section1"]["Int2"   ] << std::endl;
    std::cout << "UInt1   : " << (unsigned int       )xini_file["section1"]["UInt1"  ] << std::endl;
    std::cout << "UInt2   : " << (unsigned int       )xini_file["section1"]["UInt2"  ] << std::endl;
    std::cout << "Long1   : " << (long               )xini_file["section1"]["Long1"  ] << std::endl;
    std::cout << "Long2   : " << (long               )xini_file["section1"]["Long2"  ] << std::endl;
    std::cout << "LLong1  : " << (long long          )xini_file["section1"]["LLong1" ] << std::endl;
    std::cout << "LLong2  : " << (long long          )xini_file["section1"]["LLong2" ] << std::endl;
    std::cout << "Float1  : " << (float              )xini_file["section1"]["Float1" ] << std::endl;
    std::cout << "Float2  : " << (float              )xini_file["section1"]["Float2" ] << std::endl;
    std::cout << "Double1 : " << (double             )xini_file["section1"]["Double1"] << std::endl;
    std::cout << "Double2 : " << (double             )xini_file["section1"]["Double2"] << std::endl;

    std::cout << "----------------------------------------" << std::endl;

    // 常规的读取操作
    std::cout << "[section2]" << std::endl;
    std::cout << "Text1   : " << (const std::string &)xini_file["section2"]["Text1"  ] << std::endl;
    std::cout << "Text2   : " << (const char *       )xini_file["section2"]["Text2"  ] << std::endl;
    std::cout << "Bool1   : " << (bool               )xini_file["section2"]["Bool1"  ] << std::endl;
    std::cout << "Bool2   : " << (bool               )xini_file["section2"]["Bool2"  ] << std::endl;
    std::cout << "Int1    : " << (int                )xini_file["section2"]["Int1"   ] << std::endl;
    std::cout << "Int2    : " << (int                )xini_file["section2"]["Int2"   ] << std::endl;
    std::cout << "UInt1   : " << (unsigned int       )xini_file["section2"]["UInt1"  ] << std::endl;
    std::cout << "UInt2   : " << (unsigned int       )xini_file["section2"]["UInt2"  ] << std::endl;
    std::cout << "Long1   : " << (long               )xini_file["section2"]["Long1"  ] << std::endl;
    std::cout << "Long2   : " << (long               )xini_file["section2"]["Long2"  ] << std::endl;
    std::cout << "LLong1  : " << (long long          )xini_file["section2"]["LLong1" ] << std::endl;
    std::cout << "LLong2  : " << (long long          )xini_file["section2"]["LLong2" ] << std::endl;
    std::cout << "Float1  : " << (float              )xini_file["section2"]["Float1" ] << std::endl;
    std::cout << "Float2  : " << (float              )xini_file["section2"]["Float2" ] << std::endl;
    std::cout << "Double1 : " << (double             )xini_file["section2"]["Double1"] << std::endl;
    std::cout << "Double2 : " << (double             )xini_file["section2"]["Double2"] << std::endl;

    std::cout << "----------------------------------------" << std::endl;

    // 带默认值的读取操作
    std::cout << "[default]" << std::endl;
    std::cout << "Text1   : " << xini_file["default"]["Text1"  ](std::string("stl string")) << std::endl; // (const std::string &)
    std::cout << "Text2   : " << xini_file["default"]["Text2"  ]("c string"               ) << std::endl; // (const char *       )
    std::cout << "Bool1   : " << xini_file["default"]["Bool1"  ](true                     ) << std::endl; // (bool               )
    std::cout << "Bool2   : " << xini_file["default"]["Bool2"  ](false                    ) << std::endl; // (bool               )
    std::cout << "Int1    : " << xini_file["default"]["Int1"   ](123456                   ) << std::endl; // (int                )
    std::cout << "Int2    : " << xini_file["default"]["Int2"   ](-123456                  ) << std::endl; // (int                )
    std::cout << "UInt1   : " << xini_file["default"]["UInt1"  ](123456U                  ) << std::endl; // (unsigned int       )
    std::cout << "UInt2   : " << xini_file["default"]["UInt2"  ](0U                       ) << std::endl; // (unsigned int       )
    std::cout << "Long1   : " << xini_file["default"]["Long1"  ](123456L                  ) << std::endl; // (long               )
    std::cout << "Long2   : " << xini_file["default"]["Long2"  ](-123456L                 ) << std::endl; // (long               )
    std::cout << "LLong1  : " << xini_file["default"]["LLong1" ](123456LL                 ) << std::endl; // (long long          )
    std::cout << "LLong2  : " << xini_file["default"]["LLong2" ](-123456LL                ) << std::endl; // (long long          )
    std::cout << "Float1  : " << xini_file["default"]["Float1" ](1.23456F                 ) << std::endl; // (float              )
    std::cout << "Float2  : " << xini_file["default"]["Float2" ](-1.23456F                ) << std::endl; // (float              )
    std::cout << "Double1 : " << xini_file["default"]["Double1"](1.234567890123456789     ) << std::endl; // (double             )
    std::cout << "Double2 : " << xini_file["default"]["Double2"](-0.1234567890123456789   ) << std::endl; // (double             )

    std::cout << "----------------------------------------" << std::endl;
    std::cout << "[section1][AppendText] : " << (const char *)xini_file["section1"]["AppendText"] << std::endl;
    std::cout << "[section1][AppendText] : " << (const char *)xini_file["section1"]["AppendText"] << std::endl;
    std::cout << "[section1][AppendInt ] : " << (int         )xini_file["section1"]["AppendInt" ] << std::endl;
    std::cout << "----------------------------------------" << std::endl;
}

/**********************************************************/
/**
 * @brief 测试 INI 的数据写入操作。
 */
void test_ini_write(const std::string& xstr_file)
{
    //======================================

    {
        // 将 test.ini 克隆到 write.ini 文件,用于后续的写操作测试
        xini_file_t xini_clone(xstr_file);
        xini_clone.dump("write.ini");
    }

    xini_file_t xini_file("write.ini");

    srand((unsigned int)time(NULL));

    //======================================
    // section1

    xini_file["section1"]["Text1"] = std::string("Write text"); // (operator const std::string &)
    xini_file["section1"]["Text2"] = ""; // (operator const char *       )
    xini_file["section1"]["Bool1"] = (0 == (rand() % 2)); // (operator bool               )
    xini_file["section1"]["Bool2"] = true; // (operator bool               )
    xini_file["section1"]["Int1"] = 12345678; // (operator int                )
    xini_file["section1"]["Int1"] = -rand(); // (operator int                )
    xini_file["section1"]["UInt1"] = (unsigned int)0xFFFFFFFF; // (operator unsigned int       )
    xini_file["section1"]["UInt2"] = (unsigned int)rand(); // (operator unsigned int       )
    xini_file["section1"]["Long1"] = -12345678; // (operator long               )
    xini_file["section1"]["Long2"] = (long)-rand(); // (operator long               )
    xini_file["section1"]["LLong1"] = -1234567890123456L; // (operator long long          )
    xini_file["section1"]["LLong2"] = rand() * 100L; // (operator long long          )
    xini_file["section1"]["Float1"] = (float)(rand() / 10000.0); // (operator float              )
    xini_file["section1"]["Float2"] = (float)(-0.123456); // (operator float              )
    xini_file["section1"]["Double1"] = (double)(rand() / 1000.0); // (operator double             )
    xini_file["section1"]["Double2"] = (double)3.141592653589793; // (operator double             )

    // add a new key value
    xini_file["section1"]["NewInt"] = rand();

    //======================================
    // section3

    // add a string value
    xini_file["section3"]["NewText"] = "information";
    xini_file["section3"]["ZHText"] = "中国制造";

    //======================================
    // section4

    // add section4 and key value
    xini_file["section4"]["EmptyKey"];
    xini_file["section4"]["ValueKey"] = rand();

    // test line break
    xini_file["section4"]["LineBreak1"] = "ABCDEF\nGHIJKLMN";
    xini_file["section4"]["LineBreak2"] = "ABCDEF\rGHIJKLMN";
    xini_file["section4"]["LineBreak3"] = "ABCDEF\r7\nGHIJKLMN";
    xini_file["section4"]["LineBreak4"] = "\rABCDEFGHIJKLMN";

    //======================================

    std::cout.precision(20);
    std::cout.setf(std::ios_base::boolalpha);

    std::cout << "//====================================================================" << std::endl;
    std::cout << "test_ini_write() output: " << std::endl;
    std::cout << "----------------------------------------" << std::endl;
    std::cout << xini_file;
    std::cout << "----------------------------------------" << std::endl;

    //======================================
}

/**
 * @struct color_t
 * @brief  用于测试 INI 存储自定义的数据结构。
 */
struct color_t
{
    unsigned int r;
    unsigned int g;
    unsigned int b;
    unsigned int a;

    color_t(
        unsigned int r = 0,
        unsigned int g = 0,
        unsigned int b = 0,
        unsigned int a = 255)
    {
        this->r = r;
        this->g = g;
        this->b = b;
        this->a = a;
    }
};

/**********************************************************/
/**
 * @brief 使用 INI 键值节点的流输出操作符 存储 color_t 类型数据。
 */
xini_keyvalue_t & operator << (xini_keyvalue_t & xini_kv, const color_t & xclr)
{
    std::ostringstream ostr;
    ostr << "(" << xclr.r << ","
                << xclr.g << ","
                << xclr.b << ","
                << xclr.a << ")";
    xini_kv.set_value(ostr.str());
    return xini_kv;
}

/**********************************************************/
/**
 * @brief 使用 INI 键值节点的流输入操作符 读取 color_t 类型数据。
 */
xini_keyvalue_t & operator >> (xini_keyvalue_t & xini_kv, color_t & xclr)
{
    char xsplit;
    std::istringstream istr(xini_kv.xvalue());
    istr >> xsplit >> xclr.r >> xsplit
                   >> xclr.g >> xsplit
                   >> xclr.b >> xsplit
                   >> xclr.a >> xsplit;
    return xini_kv;
}

/**********************************************************/
/**
 * @brief 使用标准流输出操作符 输出 color_t 类型数据。
 */
std::ostream & operator << (std::ostream & ostr, const color_t & xclr)
{
    ostr << "(" << xclr.r << ","
                << xclr.g << ","
                << xclr.b << ","
                << xclr.a << ")";
    return ostr;
}

/**********************************************************/
/**
 * @brief 
 * 测试 INI 键值节点的 try_value() 操作接口,
 * 以及自定义数据类型的读写操作。
 * @note 
 * 键值节点的 try_value() 实现的功能:
 * 返回当前节点值,但若节点值为 空,则设置为指定值。
 */
void test_ini_vtry(void)
{
    xini_file_t xini_file("try_value.ini");

    std::cout.precision(20);
    std::cout.setf(std::ios_base::boolalpha);

    std::cout << "//====================================================================" << std::endl;
    std::cout << "test_ini_try() output: " << std::endl;

    std::cout << "----------------------------------------" << std::endl;
    std::cout << "try_value() test : " << std::endl;

    std::cout << "Text1   : " << xini_file["vtry"]["Text1"  ].try_value(std::string("try text")  ) << std::endl;
    std::cout << "Text2   : " << xini_file["vtry"]["Text2"  ].try_value("中国制造 INI"            ) << std::endl;
    std::cout << "Bool1   : " << xini_file["vtry"]["Bool1"  ].try_value((0 == (rand() % 2))      ) << std::endl;
    std::cout << "Bool2   : " << xini_file["vtry"]["Bool2"  ].try_value(true                     ) << std::endl;
    std::cout << "Int1    : " << xini_file["vtry"]["Int1"   ].try_value(12345678                 ) << std::endl;
    std::cout << "Int1    : " << xini_file["vtry"]["Int1"   ].try_value(-rand()                  ) << std::endl;
    std::cout << "UInt1   : " << xini_file["vtry"]["UInt1"  ].try_value((unsigned int)0xFFFFFFFF ) << std::endl;
    std::cout << "UInt2   : " << xini_file["vtry"]["UInt2"  ].try_value((unsigned int)rand()     ) << std::endl;
    std::cout << "Long1   : " << xini_file["vtry"]["Long1"  ].try_value(-12345678                ) << std::endl;
    std::cout << "Long2   : " << xini_file["vtry"]["Long2"  ].try_value((long)-rand()            ) << std::endl;
    std::cout << "LLong1  : " << xini_file["vtry"]["LLong1" ].try_value(-1234567890123456L       ) << std::endl;
    std::cout << "LLong2  : " << xini_file["vtry"]["LLong2" ].try_value(rand() * 100L            ) << std::endl;
    std::cout << "Float1  : " << xini_file["vtry"]["Float1" ].try_value((float)(rand() / 10000.0)) << std::endl;
    std::cout << "Float2  : " << xini_file["vtry"]["Float2" ].try_value((float)(-0.123456)       ) << std::endl;
    std::cout << "Double1 : " << xini_file["vtry"]["Double1"].try_value((double)(rand() / 1000.0)) << std::endl;
    std::cout << "Double2 : " << xini_file["vtry"]["Double2"].try_value((double)3.141592653589793) << std::endl;

    std::cout << "----------------------------------------" << std::endl;
    std::cout << "color_t test : " << std::endl;

    xini_file["color"]["red"  ] << color_t(255, 0, 0, 255);
    xini_file["color"]["green"] << color_t(0, 255, 0, 255);
    xini_file["color"]["blue" ] << color_t(0, 0, 255, 255);

    color_t xclr;
    xini_file["color"]["red"  ] >> xclr; std::cout << "[color][red  ] : " << xclr << std::endl;
    xini_file["color"]["green"] >> xclr; std::cout << "[color][green] : " << xclr << std::endl;
    xini_file["color"]["blue" ] >> xclr; std::cout << "[color][blue ] : " << xclr << std::endl;

    std::cout << "INI [color][red  ] : " << (const char *)xini_file["color"]["red"  ] << std::endl;
    std::cout << "INI [color][green] : " << (const char *)xini_file["color"]["green"] << std::endl;
    std::cout << "INI [color][blue ] : " << (const char *)xini_file["color"]["blue" ] << std::endl;

    std::cout << "----------------------------------------" << std::endl;
}

IniFile.h

#ifndef __INI_FILE_H__
#define __INI_FILE_H__

#include <string>

class IniFile
{
public:
	static bool read(const std::string& section, const std::string& key, std::string& value, const std::string& fileName);
	static bool write(const std::string& section, const std::string& key, const std::string& value, const std::string& fileName);
};

#endif /* __INI_FILE_H__ */

IniFile.cpp

#include <mutex>
#include "IniFile.h"
#include "xini_file.h"

static std::mutex mtx;

bool IniFile::read(const std::string& section, const std::string& key, std::string& value, const std::string& fileName)
{
	xini_file_t xini_file(fileName);

	value = (const std::string&)xini_file[section][key];

	return value.size()? true : false; 
}


bool IniFile::write(const std::string& section, const std::string& key, const std::string& value, const std::string& fileName)
{
	std::unique_lock<std::mutex> lock(mtx);

	xini_file_t xini_file(fileName);

	xini_file[section][key] = value;

	return true;
}

参考

https://gitee.com/Gaaagaa/inifile

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Ini格式文件】Ini格式文件的读写 的相关文章

  • 未提供参数时如何指定 C# System.Commandline 行为?

    在我的控制台应用程序中 当未提供控制台参数时 将执行我指定列表 在本例中为参数 3 的任何处理程序 调用该处理程序时 布尔参数设置为 false 但对我来说 根本不调用它更有意义 如何防止这种情况发生并显示帮助文本 using System
  • 注销租约抛出 InvalidOperationException

    我有一个使用插件的应用程序 我在另一个应用程序域中加载插件 我使用 RemoteHandle 类http www pocketsilicon com post Things That Make My Life Hell Part 1 App
  • Directory.Delete 之后 Directory.Exists 有时返回 true ?

    我有非常奇怪的行为 我有 Directory Delete tempFolder true if Directory Exists tempFolder 有时 Directory Exists 返回 true 为什么 可能是资源管理器打开了
  • 在 DataView 的 RowFilter 中选择 DISTINCT

    我试图根据与另一个表的关系缩小 DataView 中的行范围 我使用的 RowFilter 如下 dv new DataView myDS myTable id IN SELECT DISTINCT parentID FROM myOthe
  • C中的malloc内存分配方案

    我在 C 中尝试使用 malloc 发现 malloc 在分配了一些内存后浪费了一些空间 下面是我用来测试 malloc 的一段代码 include
  • 在 LINQ 中按 Id 连接多表和分组

    我想按categoryId显示列表产品的名称组 这是我的代码 我想要我的视图显示结果 Desktop PC HP Red PC Dell Yellow PC Asus Red SmartPhone Lumia 720 Blue 我的组模型
  • 错误:表达式不产生值

    我尝试将以下 C 代码转换为 VB NET 但在编译代码时出现 表达式不产生值 错误 C Code return Fluently Configure Mappings m gt m FluentMappings AddFromAssemb
  • 在 C 中匹配二进制模式

    我目前正在开发一个 C 程序 需要解析一些定制的数据结构 幸运的是我知道它们是如何构造的 但是我不确定如何在 C 中实现我的解析器 每个结构的长度都是 32 位 并且每个结构都可以通过其二进制签名来识别 举个例子 有两个我感兴趣的特定结构
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 使用接口有什么好处?

    使用接口有什么用 我听说它用来代替多重继承 并且还可以用它来完成数据隐藏 还有其他优点吗 哪些地方使用了接口 程序员如何识别需要该接口 有什么区别explicit interface implementation and implicit
  • 如何使用 LINQ2SQL 连接两个不同上下文的表?

    我的应用程序中有 2 个数据上下文 不同的数据库 并且需要能够通过上下文 B 中的表的右连接来查询上下文 A 中的表 我该如何在 LINQ2SQL 中执行此操作 Why 我们正在使用 SaaS 产品来跟踪我们的时间 项目等 并希望向该产品发
  • 将 Word 文档另存为图像

    我正在使用下面的代码将 Word 文档转换为图像文件 但是图片显得太大 内容不适合 有没有办法渲染图片或将图片保存到合适的尺寸 private void btnConvert Click object sender EventArgs e
  • qdbusxml2cpp 未知类型

    在使用 qdbusxml2cpp 程序将以下 xml 转换为 Qt 类时 我收到此错误 qdbusxml2cpp c ObjectManager a ObjectManager ObjectManager cpp xml object ma
  • 具有交替类型的可变参数模板参数包

    我想知道是否可以使用参数包捕获交替参数模式 例如 template
  • DbContext 和 ObjectContext 有什么区别

    From MSDN 表示工作单元和存储库模式的组合 使您能够查询数据库并将更改分组在一起 然后将这些更改作为一个单元写回存储 DbContext在概念上类似于ObjectContext 我虽然DbContext只处理与数据库的连接以及针对数
  • 等待进程释放文件

    我如何等待文件空闲以便ss Save 可以用新的覆盖它吗 如果我紧密地运行两次 左右 我会得到一个generic GDI error
  • 使用 C# 读取 Soap 消息

  • 按 Esc 按键关闭 Ajax Modal 弹出窗口

    我已经使用 Ajax 显示了一个面板弹出窗口 我要做的是当用户按 Esc 键时关闭该窗口 这可能吗 如果有人知道这一点或以前做过这一点 请帮助我 Thanks 通过以下链接 您可以通过按退出按钮轻松关闭窗口 http www codepro
  • WebSocket安全连接自签名证书

    目标是一个与用户电脑上安装的 C 应用程序交换信息的 Web 应用程序 客户端应用程序是 websocket 服务器 浏览器是 websocket 客户端 最后 用户浏览器中的 websocket 客户端通过 Angular 持久创建 并且
  • 当我使用 OpenSSL1.1.0g 根据固定的 p 和 g 值创建 Diffie Hellman 密钥协议密钥时,应该执行哪些检查?

    您好 我尝试通过这段代码使用修复 p 和 g 参数来制作 Diffie Hellman Keysanswer https stackoverflow com a 54538811 4706711 include

随机推荐

  • 关于protected权限的子类访问方式

    声明为protected权限的成员变量和成员方法 可以被同一包中的所有类和不同包中的子类访问 但是 在实际使用中 不同包中的子类要访问父类中protected权限的成员 却不是那么随意的调用 看几个例子 首先在ch13Test包中定义父类A
  • Mysql JDBC支持utf8mb4编码

    项目中需要在mysql中存储包含emoji表情的字段 使用utf8编码无法解决 因为mysql的utf8实现只有3字节 为此mysql在5 3 后加入了utf8mb4支持 完全兼容utf8 utf8mb4最多可用4字节存储一个字符 这样就可
  • hbase建表时region预分区的方法

    hbase建表时region预分区的方法 2015 06 05 13 43 866人阅读 评论 0 收藏 举报 分类 hbase 6 版权声明 本文为博主原创文章 未经博主允许不得转载 如果知道hbase数据表的key的分布情况 就可以在建
  • 【kafka性能测试脚本详解、性能测试、性能分析与性能调优】

    Kafka 性能测试 一 介绍 Apache Kafka 官方提供了两个客户端性能测试脚本 它们的存放位置如下 生产者性能测试脚本 KAFKA HOME bin kafka producer perf test sh 消费者性能测试脚本 K
  • 蓝桥杯 砝码称重 递归 解题报告

    5个砝码 用天平称重时 我们希望用尽可能少的砝码组合称出尽可能多的重量 如果只有5个砝码 重量分别是1 3 9 27 81 则它们可以组合称出1到121之间任意整数重量 砝码允许放在左右两个盘中 本题目要求编程实现 对用户给定的重量 给出砝
  • Java_开源框架_JPinyin汉字转拼音的Java开源库

    本博文为子墨原创 转载请注明出处 http blog csdn net zimo2013 article details 50039339 1 介绍 JPinyin是一个汉字转拼音的Java开源类库 在PinYin4j的功能基础上做了一些改
  • 计算24点

    题目描述 计算24点是一种扑克牌益智游戏 随机抽出4张扑克牌 通过加 减 乘 除 四种运算法则计算得到整数24 本问题中 扑克牌通过如下字符或者字符串表示 其中 小写joker表示小王 大写JOKER表示大王 3 4 5 6 7 8 9 1
  • VUE Element ui el-switch文字在开关里面显示

  • CSS经典布局 -- 圣杯布局 & 双飞翼布局

    文章目录 引言 圣杯布局 圣杯布局DOM结构 圣杯布局样式 圣杯布局总结 双飞翼布局 双飞翼布局DOM结构 双飞翼布局样式 双飞翼布局总结 总结 引言 圣杯布局和双飞翼布局都是比较经典的三栏布局 两种布局的页面效果基本相同 两种布局都是两侧
  • Tutorial: Low Power Design, Verification, and Implementation with IEEE 1801™ UPF™

    Tutorial Low Power Design Verification and Implementation with IEEE 1801 UPF Presented at DVCon 2013 on February 25 2013
  • mtk 6771 耳机底层配置分享

    42条消息 MTK6797 Accdet驱动分析总结 kerson的专栏 CSDN博客 42条消息 3 MTK 底层耳机中断上报流程 zhigouliu的博客 CSDN博客 这里分享一下本人再耳机调试的心的 理论有不懂的可以参考上面博客 耳
  • cmake之add_dependencies

    简介 add dependencies
  • 十行Python代码搞定图片中的物体检测

    Word is useless show me the pic MR Lu 先看下原图 图片表述的是一男一女在散步 后面有一辆车 现在来看下我们通过十行代码实现的效果 我们可以看到 在这幅图中其实有三个 person 被识别出来 包括后面非
  • 安装Ubuntu遇到unable to find a medium containing a live file system解决方案

    安装unable to find a medium containing a live file system 搜了好几个帖子 说是重新烧录u盘 换usb2 0 都不好使 最后找到了 在启动页面点击e 可以进入启动写参数界面 将quiet
  • vue3+vite的项目中实现右键事件的神器

    前言 vue3 vite的项目中实现右键事件的神器 imengyu vue3 context menu 实现效果 使用步骤 1 安装 npm cnpm pnpm yarn 都可以 装上下面插件 imengyu vue3 context me
  • Linux操作系统——磁盘管理

    目录 一 理论基础 二 增加虚拟磁盘 2 1 添加硬盘 6 2 2 分区 2 3 格式化 6 2 4 文件的挂载与卸载 2 5 挂载永久化 6 3 磁盘相关命令 6 3 1 命令fdisk l 6 3 2 命令df 6 3 3 命令lsbl
  • 蓝桥杯 基础训练—数列排序  给定一个长度为n的数列,将这个数列按从小到大的顺序排列。1<=n<=200

    问题描述 给定一个长度为n的数列 将这个数列按从小到大的顺序排列 1 lt n lt 200 输入格式 第一行为一个整数n 第二行包含n个整数 为待排序的数 每个整数的绝对值小于10000 输出格式 输出一行 按从小到大的顺序输出排序后的数
  • Android屏幕适配(使用ConstraintLayout),2021年Android高级面试题总结

    2 然后拉一个imageView进入布局 选择自己要显示的图片 3 将此imageView的上下参照物设置为两参照线 并将左右参照物设置为父容器 将其宽设置为wrap content 高设置为mach constraint 也就是0dp 这
  • NVIDIA vGPU License服务器安装过程

    在vGPU场景下 NVIDIA vGPU License Server 是一个很重要的组件 一个vGPU虚拟机没有正常获取License的时候 功能会受到很大限制 因此正式生产环境或者POC测试环境中都建议搭建 在之前的文章介绍过VMwar
  • 【Ini格式文件】Ini格式文件的读写

    前言 在实际工作中 常常需要读写ini格式的文件 在Gitee上找到一份开源的代码 MIT证书 其使用了文件读写的标准库函数 但我当前的嵌入式环境对文件读写的标准库函数的支持存在问题 当调用文件读写的标准库函数时会导致系统宕机 为了解决这问