许多开发者抱怨c++不能像java那样绑定properties类。java的properties类内在包含一个文件,该文件用来读写properties类中的属性,可以写成这样形式:<名字>=<数值>(例如:connecttointernet=use ie)。
使用properties类的好处就是你可以很轻松的理解和修改它们。在本文的第一部分中,你将看到我们也可以在c++中使用properties类。本文的第二部分将向你演示通过使用操作符>>和<<把数据保存到properties类中是多么的容易。
现在介绍c++ properties文件的结构。该文件的每一行可以是下面三种情况中的某一种:
空行(认为它是注释中的一部分)
以‘#’ 开始的注释行
‘<名字>=<数值>’行,这是给一个属性赋值的语句
现在让我们再看看properties类的的特点:
注释是持久性的(当保存properties类时,它们不会丢失掉)。注意每一个注释都属于某个属性。在‘<名字>=<数值>’行上的注释行属于该‘<名字>’属性。
当保存properties类后,属性仍然保留自己的位置。
它对各种字符类型都有效:char、wchar_t等等
properties类的使用相当简单:
save():保存属性
has_property(strpropertyname):如果类中有该属性则返回‘真’
string get_property(strpropertyname):返回指定的属性(如果指定属性不存在,则抛出例外)
set_property(strpropertyname, strpropertyvalue):设置给定属性
stringget_property_comment( strpropertyname):返回属于指定属性的注释(如果指定属性的注释不存在,则抛出例外)
set_property_comment(strpropertyname, strpropertycomment):设置指定属性的注释(如果指定属性的注释不存在,则抛出例外)
下面是file_reader_writer类以及相应例子的代码。运行它之后,请查看properties.txt文件。看看访问和修改它是多么容易的一件事。
#include exception>
#include string>
#include sstream>
#include map>
#include vector>
#include fstream>
#include algorithm>
#include functional>
//允许字符串转化
template< class fromchartype, class tochartype>
inline std::basic_string< tochartype> convert_string( const std::basic_string< fromchartype> & strsource)
{
std::basic_string< tochartype> strdest;
int nsourcelen = strsource.length();
strdest.resize( nsourcelen);
for ( int idxchar = 0; idxchar < nsourcelen; idxchar++)
{ strdest[ idxchar] = ( tochartype)strsource[ idxchar]; }
return strdest;
}
// 用于trim_spaces;
template< class chartype>
struct is_char_in_str : public std::binary_function< chartype, std::basic_string< chartype>, bool>
{
bool operator()( chartype ch, const std::basic_string< chartype> & strsource) const
{ return (strsource.find( ch) != std::basic_string< chartype>::npos); }
};
//消除字符串中的空格
template< class chartype>
std::basic_string< chartype> trim_spaces( const std::basic_string< chartype> & strsource)
{
std::basic_string< chartype> strspaces; strspaces += ( chartype)' '; strspaces += ( chartype)'/t';
typedef std::basic_string< chartype> string_type;
string_type::const_iterator
itfirst = std::find_if( strsource.begin(), strsource.end(),
std::not1( std::bind2nd( is_char_in_str< chartype>(), strspaces)));
string_type::const_reverse_iterator
ritlast = std::find_if( strsource.rbegin(), strsource.rend(),
std::not1( std::bind2nd( is_char_in_str< chartype>(), strspaces)));
string_type::const_iterator itlast = &*ritlast;
if ( itfirst <= itlast)
if ( itfirst != strsource.end())
return string_type( itfirst, itlast + 1);
return string_type();
}
// 当读写属性时的例外
class properties_exception : public std::exception
{
public:
properties_exception( const std::string & str) : m_strdescription( str) {}
const char * what() const { return m_strdescription.c_str(); }
private:
std::string m_strdescription;
};
// 从文件中读写属性
template< class chartype>
class file_reader_writer
{
typedef std::basic_string< chartype> string_type;
public:
// ... needed within the basic_properties!
typedef chartype char_type;
public:
file_reader_writer( const char * strfilename)
: m_bisdirty( false), m_strfilename( strfilename) { read_properties(); }
~file_reader_writer() { save(); }
void save()
{ write_properties(); }
bool has_property( const string_type & strpropertyname) const
{
propertiescollection::const_iterator itfound = m_collproperties.find( strpropertyname);
return ( itfound != m_collproperties.end());
}
const string_type & get_property( const string_type & strpropertyname) const
{
propertiescollection::const_iterator itfound = m_collproperties.find( strpropertyname);
if ( itfound != m_collproperties.end())
return itfound->second.m_strvalue;
else
throw properties_exception(
"cound not get property value for '" + convert_string< char_type, char>( strpropertyname) +
"', since this property does not exist.");
}
void set_property( const string_type & strproperty, const string_type & strpropertyvalue)
{
propertiescollection::iterator itfound = m_collproperties.find( strproperty);
if ( itfound == m_collproperties.end())
// 它是一个新的属性
m_aproperties.push_back( strproperty);
m_collproperties[ strproperty].m_strvalue = strpropertyvalue;
m_bisdirty = true;
}
const string_type & get_property_comment( const string_type & strpropertyname) const
{
propertiescollection::const_iterator itfound = m_collproperties.find( strpropertyname);
if ( itfound != m_collproperties.end())
return itfound->second.m_strcomment;
else
throw properties_exception(
"cound not get property comment for '" + convert_string< char_type, char>( strpropertyname) +
"', since this property does not exist.");
}
void set_property_comment( const string_type & strpropertyname, const string_type & strpropertycomment)
{
propertiescollection::iterator itfound = m_collproperties.find( strpropertyname);
if ( itfound != m_collproperties.end())
itfound->second.m_strcomment = strpropertycomment;
else
throw properties_exception(
"cound not set property comment for '" + convert_string< char_type, char>( strpropertyname) +
"', since this property does not exist.");
m_bisdirty = true;
}
private:
static const char_type get_delimeter() { return '='; }
static const char_type get_comment() { return '#'; }
void read_properties()
{
const char delimeter = get_delimeter();
const char comment = get_comment();
std::basic_ifstream< char_type> streamin( m_strfilename.c_str());
string_type strline;
string_type strcomment;
while ( std::getline( streamin, strline))
{
strline = trim_spaces( strline);
bool biscomment = strline.empty() || ( strline[ 0] == comment);
if ( biscomment)
{ strcomment += strline; strcomment += '/n'; }
else
{
int idxdelimeter = strline.find( delimeter);
if ( idxdelimeter != string_type::npos)
{
string_type strpropertyname = strline.substr( 0, idxdelimeter);
string_type strpropertyvalue = strline.substr( idxdelimeter + 1);
strpropertyname = trim_spaces( strpropertyname);
strpropertyvalue = trim_spaces( strpropertyvalue);
m_collproperties.insert(
std::make_pair( strpropertyname, oneproperty( strpropertyvalue, strcomment)));
m_aproperties.push_back( strpropertyname);
strcomment.erase();
}
else
throw properties_exception(
"while reading from file '" + m_strfilename +
"', we encountered an invalid line: /n" + convert_string< char_type, char>( strline));
}
}
m_strlastcomment = strcomment;
}
void write_properties() const
{
if ( !m_bisdirty)
// 无需保存
;return;
const char delimeter = get_delimeter();
std::basic_ofstream< char_type> streamout( m_strfilename.c_str());
propertiesarray::const_iterator
itfirst = m_aproperties.begin(), itlast = m_aproperties.end();
while ( itfirst != itlast)
{
const string_type & strpropertyname = *itfirst;
const oneproperty & property = m_collproperties.find( strpropertyname)->second;
write_property_comment( streamout, property.m_strcomment);
string_type strtowrite = strpropertyname;
strtowrite += ' '; strtowrite += delimeter; strtowrite += ' ';
streamout strtowrite << property.m_strvalue << std::endl;
++itfirst;
}
write_property_comment( streamout, m_strlastcomment);
m_bisdirty = false;
}
void write_property_comment( std::basic_ofstream< char_type> & streamout, const string_type & strcomment) const
{
const char comment = get_comment();
std::basic_stringstream< char_type> streamcomment( strcomment);
string_type strline;
while ( std::getline( streamcomment, strline))
{
if ( !strline.empty())
if ( strline[ 0] == comment)
streamout << strline << std::endl;
else
{
string_type strprefix;
strprefix += comment; strprefix += ' ';
streamout << strprefix << strline << std::endl;
}
else
streamout << std::endl;
}
}
private:
// 我们用来读写的文件
std::string m_strfilename;
//如果自上次保存后属性又有修改则赋值为“真”
mutable bool m_bisdirty;
struct oneproperty
{
oneproperty() {}
oneproperty( const string_type & strvalue, const string_type & strcomment)
: m_strvalue( strvalue), m_strcomment( strcomment) {}
string_type m_strvalue;
string_type m_strcomment;
};
// 属性
typedef std::map< string_type, oneproperty> propertiescollection;
propertiescollection m_collproperties;
// ……确保我们是按同样的次序保存属性
// 读属性
typedef std::vector< string_type> propertiesarray;
propertiesarray m_aproperties;
// 读取所有属性后的注释
string_type m_strlastcomment;
};
下面是用到这个类的一个例子:
#include
int main(int argc, char* argv[])
{
file_reader_writer< char> rw( "properties.txt");
rw.set_property( "app path", "c:/program files/pfs/jokexplorer");
rw.set_property_comment( "app path", "where are we installed?");
rw.set_property( "version", "4.0.0.1");
rw.set_property_comment( "version", "what's our version?");
rw.set_property( "run on startup", "1");
rw.set_property_comment( "run on startup", "are we run, when the computer starts?");
rw.set_property( "automatic logoff minutes", "60");
rw.set_property_comment( "automatic logoff minutes", "when should we deconnect from the server?");
rw.set_property( "connect to internet", "use ie");
rw.set_property_comment( "connect to internet", "how are we to connect to the internet?");
std::cout << "this is how we connect to internet: " << rw.get_property( "connect to internet") << std::endl;
return 0;
}
闽公网安备 35060202000074号