虽然微软强烈推荐用注册表代替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