INI文件解析、遍历

主宰稳场 提交于 2019-12-08 19:24:06

工作中时常需要给软件添加配置文件功能,INI文件简单又高效,但是微软的那套API使用太不方便,尤其是INI文件的遍历,所有花了一下午时间造了个轮子,自己解析INI文件。

目前只能读取Unicode小端编码,就是用windows记事本另存为时编码选择Unicode。
这里写图片描述

INI文件中元素分为节名、键和值,比如
[section]
key=value

键值必须属于某个节,节名必须放在中括号内,键和值中用等号隔开,一行只能有一对键值和一个节(就是必须分行)。本轮子内的键名可以有多个 ‘[’ 但不能有 ‘]’和 ‘=’,键中可以有 ‘[’ 但不能有 ‘]’和 ‘=’(’=’会被截断),值中可以有任何字符。空格和注释会被忽略,注释是以 ‘;’ 开头的行 。

下面是代码:

Ini.h

#pragma once

#include <afxwin.h>
#include <iostream>
#include <map>
#include <vector>

class CIni
{
public:
    // Ini文件中的键值对表
    typedef std::map<std::wstring, std::wstring> KValueTable;   
    // Ini文件中的节
    typedef struct Section
    {
        std::wstring sectionName;
        KValueTable kValueTable;
    }*PSection;
    // Ini文件中的节表
    typedef std::vector<Section> SectionTable;

    CIni();
    ~CIni();

    // 加载Ini文件,返回值  -2:文件读取错误; -1:不支持的编码; 0:正确; > 0:解析错误的行数(第几号)
    int Load(TCHAR *fileName);
    // 保存Ini文件
    bool Save(TCHAR *fileName);

    // 清空节表
    void Clear() { m_sectionTable.clear(); }

    // 添加节
    bool AddSection(TCHAR *sectionName);

    // 添加键值
    bool AddKValue(TCHAR *sectionName, TCHAR *key, TCHAR *value);

    // 通过节名和键名获取值
    const TCHAR *GetValue(TCHAR *sectionName, TCHAR *key);

    // 通过节名和键名修改值
    bool UpdateValue(TCHAR *sectionName, TCHAR *key, TCHAR *value);

    // 获取节表
    const SectionTable &GetSectionTable() const { return m_sectionTable; }

    // 解析正常返回 0,不能处理的编码返回-1, 否则返回失败的行数(目前只能解析小端unicode编码)
    int Parse(TCHAR *text, int size, SectionTable &sectionTable);

    // ini文件内容(树形式)
    std::wstring ToString();

private:
    SectionTable m_sectionTable;        // 节表
};

Ini.cpp

#include "Ini.h"

#include <fstream>

CIni::CIni()
{
}

CIni::~CIni()
{
}

int CIni::Load(TCHAR * fileName)
{
    // 清空节表
    m_sectionTable.clear();

    // 打开文件
    CFile file;
    if (!file.Open(fileName, CFile::modeRead)) { return -1; }

    // 获取文件大小
    CFileStatus fileStatus;
    file.GetStatus(fileStatus);
    int fileSize = fileStatus.m_size;

    // 读取文件
    TCHAR *text = new TCHAR[fileSize / 2 + 10];
    int readLength = file.Read(text, fileSize);
    if (readLength != fileSize) 
    { 
        file.Close();

        return -1;
    }

    // 解析文件
    int errorRow = Parse(text, fileSize / 2, m_sectionTable);

    delete[] text;

    file.Close();

    return errorRow;
}

bool CIni::Save(TCHAR * fileName)
{
    CFile file;
    if (!file.Open(fileName, CFile::modeCreate | CFile::modeWrite)) { return false; }

    const TCHAR left = _T('[');                         // 节名左中括号
    const TCHAR right = _T(']');                        // 节名右中括号
    const TCHAR equal = _T('=');                        // 等号
    const TCHAR newLine[2] = {_T('\r'), _T('\n')};      // 回车换行

    // 遍历节表
    for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
    {
        file.Write(&left, 2);
        file.Write(it->sectionName.c_str(), it->sectionName.length() * 2);
        file.Write(&right, 2);
        file.Write(newLine, 4);

        // 遍历键值表
        for (auto i = it->kValueTable.begin(); i != it->kValueTable.end(); ++i)
        {
            file.Write(i->first.c_str(), i->first.length() * 2);
            file.Write(&equal, 2);
            file.Write(i->second.c_str(), i->second.length() * 2);
            file.Write(newLine, 4);
        }
    }

    return true;
}

bool CIni::AddSection(TCHAR *sectionName)
{
    std::wstring wSectionName(sectionName);
    for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
    {
        if (it->sectionName == wSectionName)
        {
            return false;
        }
    }

    Section section;
    section.sectionName = wSectionName;
    m_sectionTable.push_back(section);

    return true;
}

bool CIni::AddKValue(TCHAR *sectionName, TCHAR *key, TCHAR *value)
{
    std::wstring wSectionName(sectionName);
    std::wstring wKey(key);
    std::wstring wValue(value);

    for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
    {
        if (it->sectionName == wSectionName)
        {
            it->kValueTable[wKey] = wValue;

            return true;
        }

    }

    return false;
}

const TCHAR *CIni::GetValue(TCHAR * sectionName, TCHAR * key)
{
    std::wstring wSectionName(sectionName);
    std::wstring wKey(key);

    for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
    {
        if (it->sectionName == wSectionName)
        {
            auto temp = it->kValueTable.find(wKey);
            if (temp == it->kValueTable.end())
            {
                return nullptr;
            }
            else
            {
                return temp->second.c_str();
            }
        }

    }

    return nullptr;
}

bool CIni::UpdateValue(TCHAR * sectionName, TCHAR * key, TCHAR * value)
{
    std::wstring wSectionName(sectionName);
    std::wstring wKey(key);
    std::wstring wValue(value);

    for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
    {
        if (it->sectionName == wSectionName)
        {
            auto temp = it->kValueTable.find(wKey);
            if (temp == it->kValueTable.end())
            {
                return false;
            }
            else
            {
                temp->second = wValue;

                return true;
            }
        }
    }

    return false;
}

int CIni::Parse(TCHAR * text, int size, SectionTable & sectionTable)
{
    TCHAR sectionName[100] = {0};       // 节名
    int sectionIndex = 0;               // 节名下标

    TCHAR key[100] = { 0 };             // 键
    int keyIndex = 0;                   // 键下标

    TCHAR value[2000] = {0};            // 值
    int valueIndex = 0;                 // 值下标

    bool isSection = false;             // 当前字符是节名
    bool isKey = false;                 // 当前字符是键
    bool isValue = false;               // 当前字符是值
    bool isComment = false;             // 当前字符是注释

    // 失败行数
    int errorRow = 0;

    // 小端
    if (*text == 0xFEFF)
    {
        ++text;
        --size;
    }

    // 大端不能解析
    if (*text == 0xFFFE)
    {
        return -1;
    }

    for (int i = 0; i < size; ++i)
    {
        if (text[i] == _T('['))
        {
            if (!isComment)
            {
                if (isKey)
                {
                    key[keyIndex] = text[i];
                    ++keyIndex;
                }
                else if (isValue)
                {
                    value[valueIndex] = text[i];
                    ++valueIndex;
                }
                else if (isSection)
                {
                    ++errorRow;
                    return errorRow;
                }
                else
                {
                    TCHAR *pTemp = text + i + 1;
                    while (!(*pTemp == _T('\r') && *(pTemp + 1) == _T('\n')) && *pTemp != 0)
                    {
                        if (*pTemp == _T('='))
                        {
                            isKey = true;
                            memset(key, sizeof(key), 0);

                            key[0] = text[i];
                            keyIndex = 1;

                            break;
                        }
                        else if (*pTemp == _T(']'))
                        {
                            TCHAR *pTemp2 = pTemp + 1;
                            while (!(*pTemp2 == _T('\r') && *(pTemp2 + 1) == _T('\n')) && *pTemp2 != 0)
                            {
                                if (*pTemp2 != _T(' '))
                                {
                                    break;
                                }

                                ++pTemp2;
                            }

                            if (*pTemp2 == 0)
                            {
                                ++errorRow;

                                return errorRow;
                            }

                            if ((*pTemp2 == _T('\r') && *(pTemp2 + 1) == _T('\n')))
                            {
                                isSection = true;
                                memset(sectionName, sizeof(sectionName), 0);
                                sectionIndex = 0;

                                break;
                            }
                        }

                        ++pTemp;
                    }

                    if ((*pTemp == _T('\r') && *(pTemp + 1) == _T('\n')) || *pTemp == 0)
                    {
                        ++errorRow;

                        return errorRow;
                    }
                }
            }
        }
        else if (text[i] == _T(']'))
        {
            if (!isComment)
            {
                if (isSection)
                {
                    sectionName[sectionIndex] = 0;
                    Section section;
                    section.sectionName = sectionName;
                    sectionTable.push_back(section);

                    isSection = false;
                    memset(sectionName, sizeof(sectionName), 0);
                    sectionIndex = 0;

                    TCHAR *pTemp = text + i + 1;
                    while (!(*pTemp == _T('\r') && *(pTemp + 1) == _T('\n')) && *pTemp != 0)
                    {
                        if (*pTemp != _T(' '))
                        {
                            break;
                        }

                        ++pTemp;
                    }

                    if (!(*pTemp == _T('\r') && *(pTemp + 1) == _T('\n')) && *pTemp != 0)
                    {
                        ++errorRow;
                        return errorRow;
                    }
                }
                else if (isKey)
                {
                    key[keyIndex] = text[i];
                    ++keyIndex;
                }
                else if (isValue)
                {
                    value[valueIndex] = text[i];
                    ++valueIndex;
                }
                else
                {
                    ++errorRow;
                    return errorRow;
                }
            }
        }
        else if (text[i] == _T('='))
        {
            if (!isComment)
            {
                if (isKey)
                {
                    key[keyIndex] = 0;
                    isKey = false;

                    if (isValue)
                    {
                        ++errorRow;
                        return errorRow;
                    }

                    isValue = true;
                }
                else if (isSection)
                {
                    sectionName[sectionIndex] = text[i];
                    ++sectionIndex;
                }
                else if (isValue)
                {
                    value[valueIndex] = text[i];
                    ++valueIndex;
                }
                else
                {
                    ++errorRow;
                    return errorRow;
                }
            }
        }
        else if ((text[i] == _T('\r') && text[i + 1] == _T('\n')))
        {
            isComment = false;

            if (isValue)
            {
                if (isSection || isKey)
                {
                    ++errorRow;
                    return errorRow;
                }

                value[valueIndex] = 0;
                std::wstring wKey(key);
                std::wstring wValue(value);
                if (sectionTable.size() <= 0) 
                {
                    ++errorRow;
                    return errorRow;
                }

                sectionTable[sectionTable.size() - 1].kValueTable[wKey] = wValue;

                memset(key, sizeof(key), 0);
                keyIndex = 0;

                isValue = false;
                memset(value, sizeof(value), 0);
                valueIndex = 0;
            }
            else if (isKey || isSection)
            {
                ++errorRow;
                return errorRow;
            }

            ++errorRow;

            ++i;
        }
        else if (text[i] == 0)
        {
            if (isValue && !isKey && !isSection)
            {
                value[valueIndex] = 0;
                std::wstring wKey(key);
                std::wstring wValue(value);
                if (sectionTable.size() <= 0)
                {
                    ++errorRow;
                    return errorRow;
                }

                sectionTable[sectionTable.size() - 1].kValueTable[wKey] = wValue;
            }
            else if (isKey)
            {
                ++errorRow;
                return errorRow;
            }
        }
        else if (isSection)
        {
            if (text[i] != _T(' '))
            {
                if (isKey || isValue)
                {
                    ++errorRow;
                    return errorRow;
                }
                else
                {
                    sectionName[sectionIndex] = text[i];
                    ++sectionIndex;
                }
            }
        }
        else if (isKey)
        {
            if (text[i] != _T(' '))
            {
                if (isSection || isValue)
                {
                    ++errorRow;
                    return errorRow;
                }
                else
                {
                    key[keyIndex] = text[i];
                    ++keyIndex;
                }
            }
        }
        else if (isValue)
        {
            if (text[i] != _T(' '))
            {
                if (isSection || isKey)
                {
                    ++errorRow;
                    return errorRow;
                }
                else
                {
                    value[valueIndex] = text[i];
                    ++valueIndex;
                }
            }
        }
        else
        {
            if (text[i] == _T(';'))
            {
                isComment = true;
            }

            if (text[i] != _T(' ') && text[i] != 0 && !isComment)
            {
                isKey = true;

                key[keyIndex] = text[i];
                ++keyIndex;
            }
        }
    }

    if (isValue)
    {
        value[valueIndex] = 0;
        std::wstring wKey(key);
        std::wstring wValue(value);
        if (sectionTable.size() <= 0)
        {
            ++errorRow;
            return errorRow;
        }

        sectionTable[sectionTable.size() - 1].kValueTable[wKey] = wValue;
    }

    if (sectionTable.size() <= 0)
    {
        return -1;
    }
    else
    {
        return 0;
    }
}

std::wstring CIni::ToString()
{
    std::wstring context;

    // 遍历节表
    for (auto it = m_sectionTable.begin(); it != m_sectionTable.end(); ++it)
    {
        context += _T("    [");
        context += it->sectionName;
        context += _T("]\n");

        // 遍历键值表
        for (auto i = it->kValueTable.begin(); i != it->kValueTable.end(); ++i)
        {
            context += _T("    |-- ");
            context += i->first;
            context += _T(" = ");
            context += i->second;
            context += _T("\n");
        }

        context += _T("\n");
    }

    return context;
}

test.cpp

#include <Ini.h>

#include <iostream>
#include <string>
#include <locale>

int main()
{
    CIni ini;
    if (0 == ini.Load(_T("test.ini")))
    {
        std::locale loc("chs");
        std::wcout.imbue(loc);
        std::wcout << ini.ToString();
    }

    return 0;
}

test.ini

[General]
AutoRun=0
AutoDownload=0
DiskCache=4
AutoShutDown=0
ShowPortal=0

[Category]
count=5
Default=2
Category0=2|4|0|已下载|C:\QQDownload|0
Category1=4|0|7|软件|C:\QQDownload\Software|0
Category2=7|0|8|音乐|C:\QQDownload\Music|0
Category3=8|0|9|游戏|C:\QQDownload\Game|0
Category4=9|0|0|电影|C:\QQDownload\Movies|0

[TaskInfo]
ShowReferPage=http://www.qq.com
ReferPage=http://www.qq.com
DefaultCategory=2
UseLastCategory=1
LastCategory=2

运行结果
这里写图片描述

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!