freewb-deb/ini_file.h

2214 lines
65 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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 <list>
#include <map>
#include <string>
#include <sstream>
#include <fstream>
#include <cassert>
////////////////////////////////////////////////////////////////////////////////
// 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;
/** 字符串修剪操作的默认字符集(空白字符集,以 isspace() 判断的字符为标准) */
static const char XCHARS_TRIM[] = " \t\n\r\f\v";
/**
* @class xini_node_t
* @brief INI 节点描述基类。
*/
class xini_node_t
{
friend class xini_file_t;
friend class xini_section_t;
friend class xini_keyvalue_t;
// common invoking
protected:
/**********************************************************/
/**
* @brief 判断是否为单行字符串。
*/
static inline bool is_sline(const std::string & xstr)
{
return (xstr.find_first_of("\r\n") == std::string::npos);
}
/**********************************************************/
/**
* @brief 判定字符串是否被修剪过。
*/
static inline bool
is_xtrim(
const std::string & xstr,
const char * xchars = XCHARS_TRIM)
{
std::string::size_type st_pos = xstr.find_first_of(xchars);
return ((std::string::npos == st_pos) ||
((st_pos > 0) && (st_pos < (xstr.size() - 1))));
}
/**********************************************************/
/**
* @brief 修剪字符串前后端的字符集。
*/
static inline std::string
trim_xstr(
const std::string & xstr,
const char * xchars = XCHARS_TRIM)
{
std::string::size_type st_pos = xstr.find_first_not_of(xchars);
if (std::string::npos != st_pos)
{
return xstr.substr(
st_pos, xstr.find_last_not_of(xchars) - st_pos + 1);
}
return std::string("");
}
/**********************************************************/
/**
* @brief 修剪字符串前端的字符集。
*/
static inline std::string
trim_lstr(
const std::string & xstr,
const char * xchars = XCHARS_TRIM)
{
std::string::size_type st_pos = xstr.find_first_not_of(xchars);
if (std::string::npos != st_pos)
{
return xstr.substr(st_pos);
}
return std::string("");
}
/**********************************************************/
/**
* @brief 修剪字符串后端的字符集。
*/
static inline std::string
trim_rstr(
const std::string & xstr,
const char * xchars = XCHARS_TRIM)
{
return xstr.substr(0, xstr.find_last_not_of(xchars));
}
/**********************************************************/
/**
* @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);
}
};
// 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 bool is_dirty(void) const
{
if (NULL != m_xowner_ptr)
{
return m_xowner_ptr->is_dirty();
}
return false;
}
/**********************************************************/
/**
* @brief 设置脏标识。
*/
virtual void set_dirty(bool x_dirty)
{
if (NULL != m_xowner_ptr)
{
m_xowner_ptr->set_dirty(x_dirty);
}
}
protected:
/**********************************************************/
/**
* @brief 重命名附属的子节点(分节节点、键值节点)的索引名。
*/
virtual bool rename_nsub(
xini_node_t * xnsub_ptr,
const std::string & xstr_name)
{
return false;
}
// 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 对象。
*
* @param [in ] xstr_line :
* 用于创建 空行节点 的字符串行,
* 其已经被 trim_xstr() 修剪前后端的空白字符。
*
* @param [in ] xowner_ptr :
* 键值节点的拥有者xini_section_t 类型)。
*
* @return xini_node_t * :
* 操作成功,返回的 空行节点;若失败,则返回 NULL 。
*/
static xini_node_t *
try_create(
const std::string & xstr_line,
xini_node_t * xowner_ptr)
{
assert(is_xtrim(xstr_line));
assert(is_sline(xstr_line));
if (!xstr_line.empty())
{
return NULL;
}
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;
}
};
////////////////////////////////////////////////////////////////////////////////
// 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 对象。
*
* @param [in ] xstr_line :
* 用于创建 注释节点 的字符串行,
* 其已经被 trim_xstr() 修剪前后端的空白字符。
*
* @param [in ] xowner_ptr :
* 键值节点的拥有者xini_section_t 类型)。
*
* @return xini_node_t * :
* 操作成功,返回的 注释节点;若失败,则返回 NULL 。
*/
static xini_node_t *
try_create(
const std::string & xstr_line,
xini_node_t * xowner_ptr)
{
assert(is_xtrim(xstr_line));
assert(is_sline(xstr_line));
if (xstr_line.empty() ||
((';' != xstr_line.at(0)) && ('#' != xstr_line.at(0))))
{
return NULL;
}
xini_comment_t * xnode_ptr = new xini_comment_t(xowner_ptr);
xnode_ptr->m_xstr_text = xstr_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;
}
// public interfaces
public:
/**********************************************************/
/**
* @brief 注释行字符串 内容。
*/
inline const std::string & text(void) const { return m_xstr_text; }
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 检查键名字符串格式是否有效。
*
* @param [in ] xstr_name :
* 待检查的 键名,其已经被 trim_xstr() 修剪过前后端的空白字符。
*/
static bool check_kname(const std::string & xstr_name)
{
assert(is_xtrim(xstr_name));
if (xstr_name.empty())
{
return false;
}
if (std::string::npos != xstr_name.find_first_of(";#=\r\n"))
{
return false;
}
if (('[' == xstr_name.at(0)) &&
(std::string::npos != xstr_name.find(']')))
{
return false;
}
return true;
}
/**********************************************************/
/**
* @brief 尝试使用字符串直接创建并初始化 xini_keyvalue_t 对象。
*
* @param [in ] xstr_line :
* 用于创建 键值节点 的字符串行,其已经被 trim_xstr() 修剪前后端的空白字符。
*
* @param [in ] xowner_ptr : 键值节点的拥有者xini_section_t 类型)。
*
* @return xini_node_t * :
* 操作成功,返回的 键值节点;若失败,则返回 NULL 。
*/
static xini_node_t *
try_create(
const std::string & xstr_line,
xini_node_t * xowner_ptr)
{
assert(is_xtrim(xstr_line));
assert(is_sline(xstr_line));
if (xstr_line.empty())
{
return NULL;
}
// 等号位置
size_t st_eq = xstr_line.find('=');
if ((0 == st_eq) || (std::string::npos == st_eq))
{
return NULL;
}
// 键名
std::string xstr_kname = trim_xstr(xstr_line.substr(0, st_eq));
if (!check_kname(xstr_kname))
{
return NULL;
}
//======================================
xini_keyvalue_t * xnode_ptr = new xini_keyvalue_t(xowner_ptr);
xnode_ptr->m_xstr_kname = xstr_kname;
xnode_ptr->m_xstr_value = trim_xstr(xstr_line.substr(st_eq + 1));
//======================================
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;
}
// template<> functions, for operators
protected:
/**********************************************************/
/**
* @brief 数值的读操作。
*/
template< typename __number_type >
__number_type get_numb(void) const
{
__number_type numb;
std::istringstream istr(m_xstr_value);
istr >> numb;
if (istr.fail())
return static_cast< __number_type >(0);
return numb;
}
/**********************************************************/
/**
* @brief 数值的读操作(带默认值)。
*/
template< typename __number_type >
__number_type get_numb(__number_type x_default) const
{
if (empty())
return x_default;
__number_type numb;
std::istringstream istr(m_xstr_value);
istr >> numb;
if (istr.fail())
return x_default;
return numb;
}
/**********************************************************/
/**
* @brief 数值的写操作。
*/
template< typename __number_type >
void set_numb(__number_type x_value)
{
std::ostringstream ostr;
ostr << x_value;
assert(!ostr.fail());
invk_set_value(ostr.str());
}
/**********************************************************/
/**
* @brief 数值的写操作。
*/
template< typename __number_type >
void set_numb(__number_type x_value, std::streamsize x_precision)
{
std::ostringstream ostr;
ostr.precision(x_precision);
ostr << x_value;
assert(!ostr.fail());
invk_set_value(ostr.str());
}
/**********************************************************/
/**
* @brief 数值的读操作(键值为 空(或格式非法)时,同步写入默认值)。
*/
template< typename __number_type >
__number_type try_numb(__number_type x_default)
{
if (empty())
{
set_numb(x_default);
return x_default;
}
__number_type numb;
std::istringstream istr(m_xstr_value);
istr >> numb;
if (istr.fail())
{
set_numb(x_default);
return x_default;
}
return numb;
}
/**********************************************************/
/**
* @brief 数值的读操作(键值为 空(或格式非法)时,同步写入默认值)。
*/
template< typename __number_type >
__number_type try_numb(__number_type x_default, std::streamsize x_precision)
{
if (empty())
{
set_numb(x_default, x_precision);
return x_default;
}
__number_type numb;
std::istringstream istr(m_xstr_value);
istr >> numb;
if (istr.fail())
{
set_numb(x_default, x_precision);
return x_default;
}
return numb;
}
/**********************************************************/
/**
* @brief bool 值的读操作(键值为 空(或格式非法)时,同步写入默认值)。
*/
bool try_bool(bool x_default)
{
//======================================
// 按 字符串 解析
if (empty())
{
invk_set_value(std::string(x_default ? "true" : "false"));
return x_default;
}
if (0 == xstr_icmp(m_xstr_value.c_str(), "true"))
return true;
if (0 == xstr_icmp(m_xstr_value.c_str(), "false"))
return false;
//======================================
// 按 整数值 解析
long numb;
std::istringstream istr(m_xstr_value);
istr >> numb;
if (istr.fail())
{
invk_set_value(std::string(x_default ? "true" : "false"));
return x_default;
}
invk_set_value(std::string((0L != numb) ? "true" : "false"));
return (0L != numb);
//======================================
}
// operators
public:
//======================================
// 基础数据类型的读操作
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 (0L != get_numb< long >());
}
operator short () const { return get_numb< short >(); }
operator unsigned short () const { return get_numb< unsigned short >(); }
operator int () const { return get_numb< int >(); }
operator unsigned int () const { return get_numb< unsigned int >(); }
operator long () const { return get_numb< long >(); }
operator unsigned long () const { return get_numb< unsigned long >(); }
operator long long () const { return get_numb< long long >(); }
operator unsigned long long () const { return get_numb< unsigned long long >(); }
operator float () const { return get_numb< float >(); }
operator double () const { return get_numb< double >(); }
operator long double () const { return get_numb< long double >(); }
//======================================
// 重载 operator (),实现带上默认值的读操作
const char * operator () (const char * x_default) const
{
if (empty())
return x_default;
return m_xstr_value.c_str();
}
bool operator () (bool x_default) 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 != get_numb< int >(x_default ? 1 : 0));
}
short operator () (short x_default) const { return get_numb< short >(x_default); }
unsigned short operator () (unsigned short x_default) const { return get_numb< unsigned short >(x_default); }
int operator () (int x_default) const { return get_numb< int >(x_default); }
unsigned int operator () (unsigned int x_default) const { return get_numb< unsigned int >(x_default); }
long operator () (long x_default) const { return get_numb< long >(x_default); }
unsigned long operator () (unsigned long x_default) const { return get_numb< unsigned long >(x_default); }
long long operator () (long long x_default) const { return get_numb< long long >(x_default); }
unsigned long long operator () (unsigned long long x_default) const { return get_numb< unsigned long long >(x_default); }
float operator () (float x_default) const { return get_numb< float >(x_default); }
double operator () (double x_default) const { return get_numb< double >(x_default); }
long double operator () (long double x_default) const { return get_numb< long double >(x_default); }
const char * operator () (const std::string & x_default) const { return this->operator ()(x_default.c_str()); }
//======================================
// 与重载的 operator () 带默认值读取操作符功能类似,
// 但键值为 空(或格式非法)时,会同步写入默认值
const char * try_value(const char * x_default)
{
if (empty())
set_value(x_default);
return m_xstr_value.c_str();
}
bool try_value(bool x_default)
{
return try_bool(x_default);
}
short try_value(short x_default) { return try_numb< short >(x_default ); }
unsigned short try_value(unsigned short x_default) { return try_numb< unsigned short >(x_default ); }
int try_value(int x_default) { return try_numb< int >(x_default ); }
unsigned int try_value(unsigned int x_default) { return try_numb< unsigned int >(x_default ); }
long try_value(long x_default) { return try_numb< long >(x_default ); }
unsigned long try_value(unsigned long x_default) { return try_numb< unsigned long >(x_default ); }
long long try_value(long long x_default) { return try_numb< long long >(x_default ); }
unsigned long long try_value(unsigned long long x_default) { return try_numb< unsigned long long >(x_default ); }
float try_value(float x_default) { return try_numb< float >(x_default, 6); }
double try_value(double x_default) { return try_numb< double >(x_default, 16); }
long double try_value(long double x_default) { return try_numb< long double >(x_default, 16); }
const char * try_value(const std::string & x_default) { return this->try_value(x_default.c_str()); }
//======================================
// 基础数据类型的写操作
xini_keyvalue_t & operator = (const char * x_value) { set_value(std::string(x_value)); return *this; }
xini_keyvalue_t & operator = (bool x_value) { invk_set_value(std::string(x_value ? "true" : "false")); return *this; }
xini_keyvalue_t & operator = (short x_value) { set_numb< short >(x_value ); return *this; }
xini_keyvalue_t & operator = (unsigned short x_value) { set_numb< unsigned short >(x_value ); return *this; }
xini_keyvalue_t & operator = (int x_value) { set_numb< int >(x_value ); return *this; }
xini_keyvalue_t & operator = (unsigned int x_value) { set_numb< unsigned int >(x_value ); return *this; }
xini_keyvalue_t & operator = (long x_value) { set_numb< long >(x_value ); return *this; }
xini_keyvalue_t & operator = (unsigned long x_value) { set_numb< unsigned long >(x_value ); return *this; }
xini_keyvalue_t & operator = (long long x_value) { set_numb< long long >(x_value ); return *this; }
xini_keyvalue_t & operator = (unsigned long long x_value) { set_numb< unsigned long long >(x_value ); return *this; }
xini_keyvalue_t & operator = (float x_value) { set_numb< float >(x_value, 6); return *this; }
xini_keyvalue_t & operator = (double x_value) { set_numb< double >(x_value, 16); return *this; }
xini_keyvalue_t & operator = (long double x_value) { set_numb< long double >(x_value, 16); return *this; }
xini_keyvalue_t & operator = (const std::string & x_value) { set_value(x_value); return *this; }
/**********************************************************/
/**
* @brief 键值节点相互赋值时,只 取值 而 忽略 键名。
*/
xini_keyvalue_t & operator = (const xini_keyvalue_t & x_value)
{
if (this != &x_value)
invk_set_value(x_value.value());
return *this;
}
//======================================
// public interfaces
public:
/**********************************************************/
/**
* @brief 键名。
*/
inline const std::string & key(void) const
{
return m_xstr_kname;
}
/**********************************************************/
/**
* @brief 键值。
*/
inline const std::string & value(void) const
{
return m_xstr_value;
}
/**********************************************************/
/**
* @brief 判断 键值 是否为 空。
*/
inline bool empty(void) const
{
return m_xstr_value.empty();
}
/**********************************************************/
/**
* @brief 修改键名。
*/
bool set_key(const std::string & xstr_key)
{
std::string xstr_kname = trim_xstr(xstr_key);
if (check_kname(xstr_kname))
{
return false;
}
return get_owner()->rename_nsub(this, xstr_kname);
}
/**********************************************************/
/**
* @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(trim_xstr(xstr));
}
// inner invoking
protected:
/**********************************************************/
/**
* @brief 设置(单行文本 且 去除头尾空白字符 的)键值。
*/
inline void invk_set_value(const std::string & xstr_value)
{
if (xstr_value != m_xstr_value)
{
m_xstr_value = xstr_value;
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;
friend class xini_keyvalue_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;
public:
typedef xlst_node_t::iterator iterator;
typedef xlst_node_t::const_iterator const_iterator;
// common invoking
protected:
/**********************************************************/
/**
* @brief 修剪 分节名字符串 前后端多余的字符。
*/
static inline std::string trim_sname(const std::string & xstr_name)
{
return trim_xstr(xstr_name, "[] \t\n\r\f\v");
}
/**********************************************************/
/**
* @brief 检查分节名字符串格式是否有效。
*
* @param [in ] xstr_name :
* 待检查的 分节名,操作前其已经被
* trim_sname() 修剪过前后端多余的字符。
*/
static inline bool check_sname(const std::string & xstr_name)
{
assert(is_xtrim(xstr_name));
return is_sline(xstr_name);
}
/**********************************************************/
/**
* @brief 尝试使用字符串直接创建并初始化 xini_section_t 对象。
*/
static xini_node_t *
try_create(
const std::string & xstr_line,
xini_node_t * xowner_ptr)
{
assert(is_xtrim(xstr_line));
assert(is_sline(xstr_line));
//======================================
if (xstr_line.empty())
{
return NULL;
}
if ('[' != xstr_line.at(0))
{
return NULL;
}
std::string::size_type st_pos = xstr_line.find(']', 1);
if (std::string::npos == st_pos)
{
return NULL;
}
//======================================
xini_section_t * xnode_ptr = new xini_section_t(xowner_ptr);
xnode_ptr->m_xstr_name = trim_xstr(xstr_line.substr(1, st_pos - 1));
// 将 自身 作为 节点 加入到 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;
}
protected:
/**********************************************************/
/**
* @brief 重命名附属的子节点(键值节点)的索引名。
* @note 该接口仅由 xini_keyvalue_t::set_key() 调用。
*/
virtual bool rename_nsub(
xini_node_t * xnsub_ptr,
const std::string & xstr_name)
{
assert(XINI_NTYPE_KEYVALUE == xnsub_ptr->ntype());
return rename_knode(
static_cast< xini_keyvalue_t * >(xnsub_ptr), xstr_name);
}
// overrides : operator
public:
/**********************************************************/
/**
* @brief 重载 operator [] 操作符,实现 键值 节点的索引操作。
*/
xini_keyvalue_t & operator [] (const std::string & xstr_key)
{
//======================================
std::string xstr_nkey = trim_xstr(xstr_key);
assert(xini_keyvalue_t::check_kname(xstr_nkey));
//======================================
xini_keyvalue_t * xndkv_ptr = find_knode(xstr_nkey);
if (NULL != xndkv_ptr)
{
return *xndkv_ptr;
}
//======================================
// 若索引的 键值节点 并未在节点表中,
// 则 新增 此 键值节点,但并不设置 脏标识,
// 避免存储不必要的 空键值节点
xndkv_ptr =
static_cast< xini_keyvalue_t * >(
xini_keyvalue_t::try_create(xstr_nkey + "=", get_owner()));
assert(NULL != xndkv_ptr);
m_xlst_node.push_back(xndkv_ptr);
m_xmap_ndkv.insert(std::make_pair(xstr_nkey, xndkv_ptr));
//======================================
return *xndkv_ptr;
}
// public interfaces
public:
/**********************************************************/
/**
* @brief 分节 名称。
*/
inline const std::string & name(void) const
{
return m_xstr_name;
}
/**********************************************************/
/**
* @brief 修改 分节 名称。
*/
bool set_name(const std::string & xstr_name)
{
std::string xstr_sname = trim_sname(xstr_name);
if (!check_sname(xstr_sname))
{
return false;
}
return get_owner()->rename_nsub(this, xstr_sname);
}
/**********************************************************/
/**
* @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) const
{
if (!m_xlst_node.empty() &&
(XINI_NTYPE_NILLINE == m_xlst_node.back()->ntype()))
{
return true;
}
return false;
}
/**********************************************************/
/**
* @brief 判定当前是否已经包含指定的 键值节点。
*/
inline bool key_included(const std::string & xstr_key) const
{
return (NULL != find_knode(trim_xstr(xstr_key)));
}
/**********************************************************/
/**
* @brief 对 键值节点 进行重命名(索引键名)操作。
*
* @param [in ] xstr_key : 目标操作的索引键名。
* @param [in ] xstr_name : 重新设置键值节点的索引键名。
*
* @return 重命名操作 是否成功。
*/
bool key_rename(const std::string & xstr_key, const std::string & xstr_name)
{
//======================================
xini_keyvalue_t * xndkv_ptr = find_knode(trim_xstr(xstr_key));
if (NULL == xndkv_ptr)
{
return false;
}
//======================================
std::string xstr_kname = trim_xstr(xstr_name);
if (!xini_keyvalue_t::check_kname(xstr_kname))
{
return false;
}
return rename_knode(xndkv_ptr, xstr_kname);
}
/**********************************************************/
/**
* @brief 删除指定键值。
*/
bool key_remove(const std::string & xstr_key)
{
//======================================
xmap_ndkv_t::iterator itmap = m_xmap_ndkv.find(trim_xstr(xstr_key));
if (itmap == m_xmap_ndkv.end())
{
return false;
}
//======================================
for (xlst_node_t::iterator
itlst = m_xlst_node.begin();
itlst != m_xlst_node.end();
++itlst)
{
if (XINI_NTYPE_KEYVALUE != (*itlst)->ntype())
continue;
if (static_cast< xini_node_t * >(itmap->second) == (*itlst))
{
delete *itlst;
m_xlst_node.erase(itlst);
break;
}
}
m_xmap_ndkv.erase(itmap);
set_dirty(true);
//======================================
return true;
}
// iterator
public:
/**********************************************************/
/**
* @brief 节点表的起始位置迭代器。
*/
inline iterator begin(void) { return m_xlst_node.begin(); }
/**********************************************************/
/**
* @brief 节点表的起始位置迭代器。
*/
inline const_iterator begin(void) const { return m_xlst_node.begin(); }
/**********************************************************/
/**
* @brief 节点表的结束位置迭代器。
*/
inline iterator end(void) { return m_xlst_node.end(); }
/**********************************************************/
/**
* @brief 节点表的结束位置迭代器。
*/
inline const_iterator end(void) const { return m_xlst_node.end(); }
/**********************************************************/
/**
* @brief 返回节点表中 首个 键值节点 的迭代器。
*/
inline iterator begin_kv(void)
{
iterator xiter = m_xlst_node.begin();
if (XINI_NTYPE_KEYVALUE == (*xiter)->ntype())
return xiter;
return next_kv(xiter);
}
/**********************************************************/
/**
* @brief 返回节点表中 首个 键值节点 的迭代器。
*/
inline const_iterator begin_kv(void) const
{
const_iterator xiter = m_xlst_node.begin();
if (XINI_NTYPE_KEYVALUE == (*xiter)->ntype())
return xiter;
return next_kv(xiter);
}
/**********************************************************/
/**
* @brief 返回 下一个 键值节点 的迭代器。
*/
iterator next_kv(iterator xiter)
{
const iterator xiter_end = m_xlst_node.end();
if (xiter != xiter_end)
{
while (++xiter != xiter_end)
if (XINI_NTYPE_KEYVALUE == (*xiter)->ntype())
return xiter;
}
return xiter_end;
}
/**********************************************************/
/**
* @brief 返回 下一个 键值节点 的迭代器。
*/
const_iterator next_kv(const_iterator xiter) const
{
const const_iterator xiter_end = m_xlst_node.end();
if (xiter != xiter_end)
{
while (++xiter != xiter_end)
if (XINI_NTYPE_KEYVALUE == (*xiter)->ntype())
return xiter;
}
return xiter_end;
}
// inner invoking
protected:
/**********************************************************/
/**
* @brief 添加(空行、注释、键值 类型的)节点。
*
* @param [in ] xnode_ptr: (空行、注释、键值 类型的)节点。
*
* @return 操作是否成功。
*/
bool push_node(xini_node_t * xnode_ptr)
{
if (NULL == 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 (NULL != find_knode(xnode_kvptr->key()))
{
return false;
}
m_xlst_node.push_back(xnode_ptr);
m_xmap_ndkv.insert(std::make_pair(xnode_kvptr->key(), xnode_kvptr));
return true;
}
return false;
}
/**********************************************************/
/**
* @brief 查找分节下的 键值 节点。
*
* @param [in ] xstr_xkey: 索引键字符串,比较时忽略大小写。
*
* @return xini_keyvalue_t *
* - 成功,返回 对应的节点;
* - 失败,返回 NULL 。
*/
xini_keyvalue_t * find_knode(const std::string & xstr_xkey) const
{
xmap_ndkv_t::const_iterator itfind = m_xmap_ndkv.find(xstr_xkey);
if (itfind != m_xmap_ndkv.end())
{
return itfind->second;
}
return NULL;
}
/**********************************************************/
/**
* @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;
}
/**********************************************************/
/**
* @brief 对 键值节点 进行重命名操作。
*
* @param [in ] xndkv_ptr : 目标操作的键值节点。
* @param [in ] xstr_name : 重新设置键值节点的索引键名。
*
* @return 重命名操作 是否成功。
*/
bool rename_knode(xini_keyvalue_t * xndkv_ptr, const std::string & xstr_name)
{
//======================================
// 与键值节点原有名称一致,忽略后续操作
if (0 == xstr_icmp(xndkv_ptr->key().c_str(), xstr_name.c_str()))
{
return true;
}
// 判定所要设置的键值节点名称,
// 与节点表中的其他键值节点名称 是否重名
if (NULL != find_knode(xstr_name))
{
return false;
}
//======================================
// 先从映射表中移除旧有的键值节点映射,
// 再对键值节点进行重命名,最后重新加入到映射表中
m_xmap_ndkv.erase(xndkv_ptr->key());
xndkv_ptr->m_xstr_kname = xstr_name;
m_xmap_ndkv.insert(std::make_pair(xndkv_ptr->key(), xndkv_ptr));
set_dirty(true);
//======================================
return true;
}
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
{
friend class xini_section_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;
public:
typedef xlst_section_t::iterator iterator;
typedef xlst_section_t::const_iterator const_iterator;
// 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 = NULL;
#define XTRY_CREATE(nptr, node, owner) \
do \
{ \
nptr = node::try_create(xstr_line, owner); \
if (NULL != 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, NULL)
, m_xbt_dirty(false)
{
}
xini_file_t(const std::string & xstr_filepath)
: xini_node_t(XINI_NTYPE_FILEROOT, NULL)
, m_xbt_dirty(false)
{
load(xstr_filepath);
}
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 bool is_dirty(void) const
{
return m_xbt_dirty;
}
/**********************************************************/
/**
* @brief 设置脏标识。
*/
virtual void set_dirty(bool x_dirty)
{
m_xbt_dirty = x_dirty;
}
protected:
/**********************************************************/
/**
* @brief 重命名附属的子节点(分节节点)的索引名。
* @note 该接口仅由 xini_section_t::set_name() 调用。
*/
virtual bool rename_nsub(
xini_node_t * xnsub_ptr,
const std::string & xstr_name)
{
assert(XINI_NTYPE_SECTION == xnsub_ptr->ntype());
return rename_sect(
static_cast< xini_section_t * >(xnsub_ptr), xstr_name);
}
// overrides : operator
public:
/**********************************************************/
/**
* @brief 从 输出流 构建 xini_file_t 内容。
*/
xini_file_t & operator << (std::istream & istr)
{
//======================================
// 记录当前操作的分节
xini_section_t * xsect_ptr = NULL;
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_line = trim_xstr(xstr_line);
// 最后一个空行不放到节点表中,避免文件关闭时 持续增加 尾部空行
if (istr.eof() && xstr_line.empty())
{
break;
}
//======================================
// 创建节点
xini_node_t * xnode_ptr = make_node(xstr_line, this);
if (NULL == 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)
{
//======================================
std::string xstr_name = xini_section_t::trim_sname(xstr_sect);
assert(xini_section_t::check_sname(xstr_name));
//======================================
xini_section_t * xsect_ptr = find_sect(xstr_name);
if (NULL != xsect_ptr)
{
return *xsect_ptr;
}
//======================================
// 若索引的分节并未在 分节 的节点表中,
// 则 新增 此分节,但并不设置 脏标识,
// 避免存储不必要的 空分节
xsect_ptr =
static_cast< xini_section_t * >(
xini_section_t::try_create("[" + xstr_name + "]", this));
assert(NULL != 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 将当前文件根下的所有节点直接输出到文件中。
*/
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 释放对象资源(可以不显示调用,对象析构函数中会自动调用该接口)。
*/
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 判定当前是否包含指定的 分节。
*/
inline bool sect_included(const std::string & xstr_sect) const
{
return (NULL != find_sect(xini_section_t::trim_sname(xstr_sect)));
}
/**********************************************************/
/**
* @brief 对 分节 进行重命名操作。
*
* @param [in ] xstr_sect : 目标操作的分节名称。
* @param [in ] xstr_name : 重新设置分节的名称。
*
* @return 重命名操作 是否成功。
*/
bool sect_rename(
const std::string & xstr_sect,
const std::string & xstr_name)
{
//======================================
xini_section_t * xsect_ptr =
find_sect(xini_section_t::trim_sname(xstr_sect));
if (NULL == xsect_ptr)
{
return false;
}
//======================================
std::string xstr_sname = xini_section_t::trim_sname(xstr_name);
if (!xini_section_t::check_sname(xstr_sname))
{
return false;
}
return rename_sect(xsect_ptr, xstr_sname);
}
/**********************************************************/
/**
* @brief 删除指定分节。
*/
bool sect_remove(const std::string & xstr_sect)
{
//======================================
xmap_section_t::iterator itmap =
m_xmap_sect.find(xini_section_t::trim_sname(xstr_sect));
if (itmap == m_xmap_sect.end())
{
return false;
}
//======================================
for (xlst_section_t::iterator
itlst = m_xlst_sect.begin();
itlst != m_xlst_sect.end();
++itlst)
{
if (itmap->second == (*itlst))
{
delete *itlst;
m_xlst_sect.erase(itlst);
break;
}
}
m_xmap_sect.erase(itmap);
set_dirty(true);
//======================================
return true;
}
// iterator
public:
/**********************************************************/
/**
* @brief 分节表的起始位置迭代器。
*/
inline iterator begin(void) { return m_xlst_sect.begin(); }
/**********************************************************/
/**
* @brief 分节表的起始位置迭代器。
*/
inline const_iterator begin(void) const { return m_xlst_sect.begin(); }
/**********************************************************/
/**
* @brief 分节表的结束位置迭代器。
*/
inline iterator end(void) { return m_xlst_sect.end(); }
/**********************************************************/
/**
* @brief 分节表的结束位置迭代器。
*/
inline const_iterator end(void) const { return m_xlst_sect.end(); }
// inner invoking
protected:
/**********************************************************/
/**
* @brief 查找分节。
*/
xini_section_t * find_sect(const std::string & xstr_sect) const
{
xmap_section_t::const_iterator itfind = m_xmap_sect.find(xstr_sect);
if (itfind != m_xmap_sect.end())
{
return itfind->second;
}
return NULL;
}
/**********************************************************/
/**
* @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 (NULL == 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;
}
/**********************************************************/
/**
* @brief 对 分节 进行重命名操作。
*
* @param [in ] xsect_ptr : 目标操作的分节。
* @param [in ] xstr_name : 重新设置分节的名称。
*
* @return 重命名操作 是否成功。
*/
bool rename_sect(xini_section_t * xsect_ptr, const std::string & xstr_name)
{
//======================================
// 与分节原有名称一致,忽略后续操作
if (0 == xstr_icmp(xsect_ptr->name().c_str(), xstr_name.c_str()))
{
return true;
}
// 判定所要设置的分节名称,
// 与分节表中的其他分节名称 是否重名
if (NULL != find_sect(xstr_name))
{
return false;
}
//======================================
// 先从映射表中移除旧有的分节节点映射,
// 再对分节进行重命名,最后重新加入到映射表中
m_xmap_sect.erase(xsect_ptr->name());
xsect_ptr->m_xstr_name = xstr_name;
m_xmap_sect.insert(std::make_pair(xsect_ptr->name(), xsect_ptr));
set_dirty(true);
//======================================
return true;
}
// 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__