现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

一个通用ini配置文件操作类

2014-03-18 06:15 工业·编程 ⁄ 共 7895字 ⁄ 字号 暂无评论

在windows平台下,简单的程序可以通过ini文件实现简单的配置,简单适用。在win32 sdk还提供了相应的api来读取修改ini文件。ini配置文件格式为:

[section]

key=string

...

读取与修改string的api为ReadPrivateProfilesString和WritePrivateProfilesString。不过美中不足的是,win32 api并没有提供删除section或key的功能,也不支持linux平台。因此我们得自己来实现。

对于ini配置文件,格式比较简单,也就是字符串分析,为了简单,只要分析行字符串即可。但分析时需要关注的功能点:忽略大小写功能,section和key前后的无用的空格功能,注释功能。读写删除字符串、整型等。因此类的设计如下:

class IniFile

{

public:

explicit IniFile(const string &fileName);

~IniFile(void);

bool ReadFile(void);

string ReadString( const string §ion, const string &key, const string &value );

int ReadInt( const string §ion, const string &key, int value );

bool WriteString( const string §ion, const string &key, const string &value );

bool WriteInt( const string §ion, const string &key, int value );

bool RemoveSection( const string §ion );

bool RemoveKey( const string §ion, const string &key );

bool WriteFile(void);

private:

static string Trim( const string &str );

static string LTrim( const string &str );

static string RTrim( const string &str );

private:

string m_fileName;

vector<string> m_vctLine;

bool m_modifyFlag;

};

IniFile类的设计,对外提供的接口基本都是C++的基本类型。首先我们来看构造和析构函数。

IniFile::IniFile(const string &fileName)

:m_fileName(fileName),

m_modifyFlag(false)

{

ReadFile();

}

IniFile::~IniFile(void)

{

WriteFile();

}

bool IniFile::ReadFile( void )

{

ifstream in(m_fileName.c_str());

if( false == in.is_open() )

return false;

string line;

while( getline(in,line) )

{

m_vctLine.push_back(line);

}

m_modifyFlag = false;

return true;

}

bool IniFile::WriteFile( void )

{

//check if is need to save

if( false == m_modifyFlag )

{

return true;

}

ofstream out(m_fileName.c_str());

for( size_t i = 0; i < m_vctLine.size(); ++i )

{

out<<m_vctLine[i]<<endl;

}

m_modifyFlag = false;

return true;

}

ReadFile和WriteFile函数用来读配置文件和保存配置文件。也比较简单。配置文件保存到内存,就是设计字符串数组。IniFile类主要的函数是ReadString和WriteString,下面主要讲述这两个函数实现。ReadString函数,参数就是section、key、value,这里的value是缺省值(参考win api接口设计)。先比较section,然后再是key和value。分析section的关键是“[]”,key=value格式也比较好分析,分析到section就终止key的分析,再回过头来分析下一个section。这里的分析,也可以用递归,这样遇到下个section就不要回退一步。一起先看一下函数代码:

string IniFile::ReadString( const string §ion, const string &key, const string &defval )

{

for( size_t i = 0;i < m_vctLine.size(); ++i )

{

string section_line = LTrim(m_vctLine[i]);

size_t sec_begin_pos = section_line.find('[');

if( sec_begin_pos == string::npos || sec_begin_pos != 0 )

{

continue;

}

size_t sec_end_pos = section_line.find( ']', sec_begin_pos );

if( sec_end_pos == string::npos )

{

continue;

}

if( ci_string(section.c_str()) != Trim(section_line.substr(sec_begin_pos+1, sec_end_pos-sec_begin_pos-1) ).c_str() )

{

continue;

}

//find key

for( ++i; i < m_vctLine.size(); ++i )

{

string key_line = LTrim(m_vctLine[i]);

size_t sec_pos = key_line.find('[');

if( sec_pos != string::npos && sec_pos == 0 )

{

--i; //reback a step,find again

break;//the line is section line

}

if( key_line.find('#') != string::npos )

{

continue;//this is comment line

}

size_t equal_pos = key_line.find( '=' );

if( equal_pos == string::npos )

{

continue;

}

if( ci_string(key.c_str()) != RTrim(key_line.substr( 0, equal_pos ) ).c_str() )

{

continue;

}

size_t comment_pos = key_line.find( "#", equal_pos + 1 );

if( comment_pos != string::npos )

{

return Trim(key_line.substr( equal_pos + 1, comment_pos - equal_pos - 1 ));

}

return Trim(key_line.substr( equal_pos + 1 ));

}

}

return defval;

}

ReadString函数中,key=value后面还可能有注释,这里注释是以"#"开头,其他形式注释暂时不支持。另外读写字符串,还要区分有引号和无引号两种形式(代码中未体现,各位读者自己增加代码)。再来看看WriteString函数,WriteString函数和ReadString差不多,只是一个读一个写,如果找不到,就要新建key=value,或者新建section和key=value。具体代码如下:

bool IniFile::WriteString( const string §ion, const string &key, const string &value )

{

for( size_t i = 0;i < m_vctLine.size(); ++i )

{

string section_line = LTrim(m_vctLine[i]);

size_t sec_begin_pos = section_line.find('[');

if( sec_begin_pos == string::npos || sec_begin_pos != 0 )

{

continue;

}

size_t sec_end_pos = section_line.find( ']', sec_begin_pos );

if( sec_end_pos == string::npos )

{

continue;

}

if( ci_string(section.c_str()) != RTrim(section_line.substr(sec_begin_pos+1, sec_end_pos-sec_begin_pos-1)).c_str() )

{

continue;

}

//find key

for( ++i; i < m_vctLine.size(); ++i )

{

string key_line = LTrim(m_vctLine[i]);

size_t sec_pos = key_line.find('[');

if( sec_pos != string::npos && sec_pos == 0 )

{

--i; //reback a step,find again

break;//the line is section line

}

if( key_line.find('#') != string::npos )

{

continue;//this is comment line

}

size_t equal_pos = key_line.find( '=' );

if( equal_pos == string::npos )

{

continue;

}

if( ci_string(key.c_str()) != RTrim(key_line.substr( 0, equal_pos )).c_str() )

{

continue;

}

size_t comment_pos = key_line.find( "#", equal_pos + 1 );

string new_line = key_line.substr( 0, equal_pos + 1 ) + value;

if( comment_pos != string::npos )

{

new_line += key_line.substr( comment_pos );

}

key_line = new_line;

m_modifyFlag = true;

return true;

}

//add a new key

m_vctLine.insert( m_vctLine.begin() + i, key + "=" + value );

m_modifyFlag = true;

return true;

}

//add a new section and a new key

m_vctLine.insert( m_vctLine.end(), "" );

m_vctLine.insert( m_vctLine.end(), "[" + section + "]" );

m_vctLine.insert( m_vctLine.end(), key + "=" + value );

m_modifyFlag = true;

return true;

}

RemoveSection函数,就是删除一个section,包括下面所有的key,代码如下:

bool IniFile::RemoveSection( const string §ion )

{

for( size_t i = 0;i < m_vctLine.size(); ++i )

{

string section_line = LTrim(m_vctLine[i]);

size_t sec_begin_pos = section_line.find('[');

if( sec_begin_pos == string::npos || sec_begin_pos != 0 )

{

continue;

}

size_t sec_end_pos = section_line.find( ']', sec_begin_pos );

if( sec_end_pos == string::npos )

{

continue;

}

if( ci_string(section.c_str()) != RTrim(section_line.substr(sec_begin_pos+1, sec_end_pos-sec_begin_pos-1)).c_str() )

{

continue;

}

//

size_t del_begin = i;

for( ++i ; i < m_vctLine.size(); ++i )

{

string next_section = LTrim(m_vctLine[i]);

size_t next_pos = next_section.find('[');

if( next_pos == string::npos || next_pos != 0 )

{

continue;

}

break;

}

m_vctLine.erase( m_vctLine.begin() + del_begin, m_vctLine.begin()+i );

return true;

}

return false;

}

RemoveKey函数就是删除一个key,这个也比较容易看。

bool IniFile::RemoveKey( const string §ion, const string &key )

{

for( size_t i = 0;i < m_vctLine.size(); ++i )

{

string §ion_line = m_vctLine[i];

size_t sec_begin_pos = section_line.find('[');

if( sec_begin_pos == string::npos || sec_begin_pos != 0 )

{

continue;

}

size_t sec_end_pos = section_line.find( ']', sec_begin_pos );

if( sec_end_pos == string::npos )

{

continue;

}

if( ci_string(section.c_str()) != Trim(section_line.substr(sec_begin_pos+1, sec_end_pos-sec_begin_pos-1)).c_str() )

{

continue;

}

//find key

for( ++i ; i < m_vctLine.size(); ++i )

{

string key_line = m_vctLine[i];

key_line = Trim(key_line);

if( key_line.find('#') == 0 )

{

continue;

}

size_t key_pos = key_line.find('=');

if( key_pos == string::npos )

{

continue;

}

if( ci_string(key.c_str()) == Trim(key_line.substr(0, key_pos)).c_str() )

{

m_vctLine.erase( m_vctLine.begin() + i );

return true;

}

}

}

return false;

}

ReadInt和WriteInt就更简单了,就通过ReadString和WriteString来实现了。直接贴上代码。

int IniFile::ReadInt( const string §ion, const string &key, int value )

{

string str = ReadString( section, key, "" );

if( "" == str )

{

return value;

}

istringstream in( str.c_str() );

int ret = 0;

in>>ret;

return ret;

}

bool IniFile::WriteInt( const string §ion, const string &key, int value )

{

ostringstream out;

out<<value;

return WriteString( section, key, out.str() );

}

其他几个辅助删除前向空格和后向空格的函数,也一并贴出来。

string IniFile::LTrim( const string &str )

{

size_t pos = 0;

while( pos != str.size() )

{

if( ' ' == str[pos] )

{

++pos;

}

else

{

break;

}

}

return str.substr(pos);

}

string IniFile::RTrim( const string &str )

{

size_t pos = str.size() - 1;

while( pos >= 0 )

{

if(' ' == str[pos])

{

--pos;

}

else

{

break;

}

}

return str.substr( 0, pos + 1 );

}

string IniFile::Trim(const string &str)

{

return LTrim( RTrim(str) );

}

不区分大小写的string类型,实现如下:

/************************************************************************/

/* 字符串的迭代器定义 */

/************************************************************************/

struct ci_char_traits : public char_traits<char>

{

static bool eq(char c1, char c2)

{

return toupper(c1) == toupper(c2);

}

static bool ne(char c1, char c2)

{

return toupper(c1) != toupper(c2);

}

static bool lt(char c1, char c2)

{

return toupper(c1) < toupper(c2);

}

static bool compare(const char* s1, const char* s2, size_t n)

{

#ifdef WIN32

return memicmp(s1,s2,n) != 0; //实现不区分大小写的串比较

#else

//linux不支持memicmp,自定义版本

char *tmps1 = new char[n];

char *tmps2 = new char[n];

for( size_t i = 0; i < n; ++i )

{

tmps1[i] = toupper(s1[i]);

tmps2[i] = toupper(s2[i]);

}

return memcmp(tmps1, tmps2, n) != 0;

#endif

}

static const char* find(const char*s, int n, char a)

{

while (n-- > 0 && toupper(*s) != toupper(a) )

++s;

return s;

}

};

使用代码如下:

int _tmain(int argc, _TCHAR* argv[])

{

IniFile ini("./test.ini");

ini.WriteString("system", "ip", "127.0.0.1");

cout<<ini.ReadString("system", "ip", "0.0.0.0")<<endl;

cout<<ini.ReadInt("system", "port", 5060 )<<endl;

return 0;

}

大功告成,各位读者自己拷贝下代码试试。

来源:KiteRunner

给我留言

留言无头像?