0CCh Blog

一个解析INI文件的类

虽然微软强烈推荐用注册表代替ini来记录软件配置,但是由于写ini文件的方便性和可读性的优势,还是让很多程序员选择把配置记录到ini文件中。但是用Windows API操作ini文件有个缺点,就是每次调用如GetPrivateProfileInt,WritePrivateProfileString等函数,都会产生一次文件打开关闭以及读写操作,并且对ini文件重新解析,这是非常低效的。所以如果需要大量的操作ini文件,例如需要读取很多配置信息以启动软件,那么这样的用法无疑会增加软件的冷启动时间。为了解决这个问题,我们就需要自己写一个模块,他能够一次性读取并且解析好ini文件。在需要读取的时候直接从内存读取,需要些的时候先全部写到内存里,最后在刷新到文件上。所以我写了一个ParseIni的类,来完成这个工作。代码如下:

#ifndef __PARSE_INI_H__
#define __PARSE_INI_H__

#include <vector>
#include <string>
#include <fstream>
#include <windows.h>

#define INI_KEY_LINE 1
#define INI_SECTION_LINE 2
#define INI_COMMENT_LINE 3
#define INI_NC_KEY_LINE 4
#define INI_WORNG_SYNTAX 0

using namespace std;


class CParseIniA {

public:
CParseIniA() {}
~CParseIniA() {}

BOOL Open(const string &IniPath;, BOOL OpenAlways = TRUE);
vector EnumSectionNames();
vector EnumKeyNamesInSection(const string &SectionName;);
string GetSectionKeyValue(const string &SectionName;, const string &KeyName;, const string &DefaultValue;);
int GetSectionKeyValueInt(const string &SectionName;, const string &KeyName;, int DefaultValue);
BOOL SetSectionKeyValueInt(const string &SectionName;, const string &KeyName;, int value, BOOL CreateNew = TRUE);
BOOL SetSectionKeyValue(const string &SectionName;, const string &KeyName;, const string &Value;, BOOL CreateNew = TRUE);
BOOL Flush();
VOID Close();

private:
ULONG SyntaxCheck(const string &KeyLine;);
vector::iterator CreateSection(const string &SectionName;);
VOID CreateKeyValue(vector::iterator it, const string &KeyName;, const string &Value;);
BOOL IsSection(string &Line;);
BOOL SetKeyValue(string &Line;, const string &Value;);
string GetKeyValue(string &Line;);
string GetSectionName(string &Line;);
string GetKeyName(string &Line;);
string trim(const string& s ,const string& drop = " ");
vector m_IniContext;
string m_IniPath;
};

inline BOOL CParseIniA::Open( const string &IniPath;, BOOL OpenAlways)
{
ifstream IniFile(IniPath.c_str());
INT FileSize;
vector::iterator it;


if (!IniFile.is_open()) {

if (!OpenAlways) {

return FALSE;
}

m_IniPath = IniPath;

return TRUE;

}

m_IniPath = IniPath;

IniFile.seekg(0, std::ios_base::end);
FileSize = IniFile.tellg();
IniFile.seekg(0, std::ios_base::beg);
if (FileSize == 0) {

return TRUE;
}


while (IniFile) {
string IniLine;
getline(IniFile, IniLine);

m_IniContext.push_back(IniLine);
}

it = m_IniContext.end();
it--;
while (trim(*it).empty()) {

m_IniContext.pop_back();
it = m_IniContext.end();
it--;
}

return TRUE;
}

inline BOOL CParseIniA::IsSection( string &Line; )
{
string SectionLine = trim(Line);
BOOL Ret = FALSE;

if (SectionLine[0] == '[' && SectionLine[SectionLine.length() - 1] == ']') {

Ret = TRUE;
}

return Ret;
}

inline vector CParseIniA::EnumSectionNames()
{
vector SectionNames;
vector::iterator it;

for (it = m_IniContext.begin(); it != m_IniContext.end(); ++it) {

if (IsSection(*it)) {

SectionNames.push_back(GetSectionName(*it));
}
}

return SectionNames;
}

inline vector CParseIniA::EnumKeyNamesInSection(const string &SectionName; )
{
vector::iterator it;
vector KeyNames;
ULONG ScanState = 0;

for (it = m_IniContext.begin(); it != m_IniContext.end(); ++it) {

if (ScanState == 0) {

if (!IsSection(*it) || GetSectionName(*it) != SectionName) {

continue;
}

ScanState = 1;
}
else if (ScanState == 1) {

if (IsSection(*it)) {

break;
}

KeyNames.push_back(GetKeyName(*it));
}
}

return KeyNames;
}

inline string CParseIniA::GetSectionName( string &Line; )
{
INT Count = Line.length();
INT i;
BOOL Start = FALSE;
string SectionName;

for (i = 0; i < Count; i++) {

if (Line[i] == '[') {

Start = TRUE;
}
else if (Line[i] == ']') {

if (Start) {

break;
}
}
else {

if (Start) {

SectionName += Line[i];
continue;
}
}
}

return SectionName;
}

inline string CParseIniA::GetKeyName( string &Line; )
{
string KeyName;

KeyName = Line.substr(0, Line.find_first_of('='));

return trim(KeyName);
}

inline string CParseIniA::trim(const string& s, const string& drop)
{
string t(s);
string r = t.erase(t.find_last_not_of(drop) + 1);
return r.erase(0,r.find_first_not_of(drop));
}


inline int CParseIniA::GetSectionKeyValueInt( const string &SectionName;, const string &KeyName;, int DefaultValue )
{
char DefaultValueString[32] = {0};
sprintf_s(DefaultValueString, "%d", DefaultValue);
string ValueString = GetSectionKeyValue(SectionName, KeyName, DefaultValueString);
return atoi(ValueString.c_str());
}

inline string CParseIniA::GetSectionKeyValue(const string &SectionName;, const string &KeyName;, const string &DefaultValue; )
{
vector::iterator it;
ULONG ScanState = 0;
string Value = DefaultValue;

for (it = m_IniContext.begin(); it != m_IniContext.end(); ++it) {

if (ScanState == 0) {

if (!IsSection(*it) || GetSectionName(*it) != SectionName) {

continue;
}

ScanState = 1;
}
else if (ScanState == 1) {

if (IsSection(*it)) {

break;
}

if (SyntaxCheck(*it) != INI_KEY_LINE) {

continue;
}

if (GetKeyName(*it) == KeyName) {

Value = GetKeyValue(*it);
}
}
}

return Value;
}

inline string CParseIniA::GetKeyValue( string &Line; )
{
string KeyName;

KeyName = Line.substr(Line.find_first_of('=') + 1);

return trim(KeyName);
}

inline BOOL CParseIniA::SetKeyValue( string &Line;, const string &Value; )
{
INT Pos = Line.find_first_of('=');

if (Pos == string::npos) {

return FALSE;
}

Pos = Line.find_first_not_of(' ', Pos + 1);

if (Pos == string::npos) {

Pos = Line.find_first_of('=') + 1;
}

Line.erase(Pos);
Line += Value;

return TRUE;
}

inline BOOL CParseIniA::SetSectionKeyValueInt( const string &SectionName;, const string &KeyName;, int Value, BOOL CreateNew /*= TRUE*/ )
{
char ValueString[32] = {0};
sprintf_s(ValueString, "%d", Value);
return SetSectionKeyValue(SectionName, KeyName, ValueString, CreateNew);
}

inline BOOL CParseIniA::SetSectionKeyValue( const string &SectionName;, const string &KeyName;, const string &Value;, BOOL CreateNew )
{
vector::iterator it;
ULONG ScanState = 0;
BOOL Ret = FALSE;

for (it = m_IniContext.begin(); it != m_IniContext.end(); ++it) {

if (ScanState == 0) {

if (!IsSection(*it) || GetSectionName(*it) != SectionName) {

continue;
}

ScanState = 1;
}
else if (ScanState == 1) {

if (IsSection(*it)) {

break;
}

if (SyntaxCheck(*it) == INI_KEY_LINE || SyntaxCheck(*it) == INI_NC_KEY_LINE) {

if (GetKeyName(*it) == KeyName) {

Ret = SetKeyValue(*it, Value);
}
}
}
}

if (CreateNew && !Ret) {

if (ScanState == 0) {

it = CreateSection(SectionName);
CreateKeyValue(it, KeyName, Value);
}
else if (ScanState == 1) {

it--;
CreateKeyValue(it, KeyName, Value);
}

Ret = TRUE;
}

return Ret;
}

inline BOOL CParseIniA::Flush()
{
ofstream IniFile(m_IniPath.c_str());
vector::iterator it;

if (!IniFile.is_open()) {

return FALSE;
}

for (it = m_IniContext.begin(); it != m_IniContext.end(); ++it) {

IniFile << it->c_str() << endl;
}

return TRUE;
}

inline VOID CParseIniA::Close()
{
}

inline vector::iterator CParseIniA::CreateSection( const string &SectionName; )
{
string FullSectionName;
vector::iterator it;

FullSectionName += '[';
FullSectionName += SectionName;
FullSectionName += ']';

m_IniContext.push_back(FullSectionName);
it = m_IniContext.begin() + m_IniContext.size() - 1;

return it;
}

inline VOID CParseIniA::CreateKeyValue( vector::iterator it, const string &KeyName;, const string &Value; )
{
string KeyInfo;

KeyInfo += KeyName;
KeyInfo += " = ";
KeyInfo += Value;
while (it != m_IniContext.begin()) {

if (!trim(*it).empty()) {

break;
}

it--;
}

m_IniContext.insert(it + 1, KeyInfo);
}

inline ULONG CParseIniA::SyntaxCheck( const string &KeyLine; )
{
string Line = trim(KeyLine);
INT Pos, CommentPos1, CommentPos2;
string KeyName;
string Value;

if (IsSection(Line)) {

return INI_SECTION_LINE;
}
else if (Line[0] == ';' || Line[0] == '#') {

return INI_COMMENT_LINE;
}
else {

Pos = Line.find_first_of('=');
if (string::npos == Pos) {

return INI_WORNG_SYNTAX;
}

KeyName = trim(Line.substr(0, Pos));
if (KeyName.empty()) {

return INI_WORNG_SYNTAX;
}

Value = trim(Line.substr(Pos + 1));
if (Value.empty()) {

return INI_NC_KEY_LINE;
}

CommentPos1 = Value.find_first_of(';');
CommentPos2 = Value.find_first_of('#');
if (CommentPos1 == string::npos && CommentPos2 == string::npos) {

return INI_KEY_LINE;
}

CommentPos1 = CommentPos1 < CommentPos2 ? CommentPos1 : CommentPos2;

if (Value.erase(CommentPos1).empty()) {

return INI_NC_KEY_LINE;
}

return INI_KEY_LINE;
}
}

#endif