正在加载小宠物
梦回起点
做你害怕做的事,你会发现:不过如此
本站基于WordPress—主题by 设计窝 \n
冀ICP备15003737号
梦回起点
Copyright © 2015-2024 All rights reserved.

内存解析Ini文件(Linux解析解析ini文件)

有没有遇到这种情况:1.文件是一个ini格式,但是不想存成文件。2.在Linux(或其他非windows平台)遇到ini文件需要读取。怎么办? 经过十来个小时的编码,改进,编码,改进。。。。。完成了下面的代码。我是在内存中解析的ini文件,如果你不需要内存中解析,那么可以稍微修改一下。废话不多说,上代码吧。效率不是很高,改进也没太在效率上改进,但能用。 四个文件,如下:拷贝过去直接用就可以了  
#ifndef INIMEMORY_H
#define INIMEMORY_H

#include <string>
#include <list>
/*********************************************************************************************************
*    程序功能:内存解析ini文件
*    作者:独行
*    时间:2017.9.5
*    目标: 1.保证正确解析ini文件,可以正常读取ini文件中指定属性的值;
*          2.解析修改ini文件,保证修改后的文件可以被其他程序/windows API识别。
*    备注: 1.程序不支持多线程,多线程程序使用需要小心;
*          2.向类传入的缓冲区要保证修改后空间足够使用,如果空间不足,会造成缓冲区溢出;
*          3.如果文件中有注释,那么对文件执行写操作之后所有注释都会消失;
*          4.程序如果进行过写入操作,那么所有段名,变量名全部都会变成小写,且所有空格 tab都会消失;
*          5.程序如果进行过写入操作,文件中的\r\n一律会变成\n;
*          6.程序传递std::string类型的参数,如果是空,一定不要出现NULL 而要使用"",因为传递的是引用,传递NULL会出错。
* —————————————————————————————————————————————————————————————————————————————————————————————————————————
* 更新:
*    2017/9/13:
*       因为发现在开机右下角程序中直接传入CStringA的buffer到本类中,CStringA对象销毁,VS会报错,所以
*       修改程序,不再将传入的Buffer作为内部Buffer了,而自己重新申请一个Buffer,自己管理配置文件的存储
*       空间将原来Buffer的内容复制过去,效率低,但是不会出错。
* 已知遗留问题:
*       拷贝构造函数需要重写
*       =重载需要重写
*       需要进一步测试
* —————————————————————————————————————————————————————————————————————————————————————————————————————————
* 更新:
*   2017/9/13:
*       重新改为使用原始内存,但是这次不同的是内部不再保存原来的内存地址,解析完直接扔掉,不知道CString
*       会不会出现问题,没有测试,这样类内部不再保存Buffer,而是直接保存一个Ini格式的数据结构
*********************************************************************************************************/

class IniMemoryPrivate;

class IniMemory
{
public:
    //构造析构
    IniMemory();
    IniMemory(char* strIniBuffer);
    IniMemory(IniMemory& iniMemory);
    ~IniMemory();

    //运算符的重载
    IniMemory& operator=(char*);
    IniMemory& operator=(const IniMemory&);

    /*
    功能:重新设置buffer
    参数:
        strIniBuffer   新的配置文件
    返回值:
        是否重新设置成功
    */
    bool SetBuffer(char* strIniBuffer);

    /*
    功能:从INI文件中获取字符串数据,不需要考虑数据的长度
    参数:
        const std::string&  strAppName   段的名字
        const std::string&  strKeyName   属性的名字
        const std::string&  strDefault   默认值,如果这个属性不存在,那么返回这个值
    返回值:
        返回strKeyName对应的值,如果该strKeyName不存在,那么返回strDefault
    */
    std::string GetIniString(const std::string& strAppName, const std::string& strKeyName, const std::string& strDefault);


    /*
    功能:从INI文件中获取一个数值
    参数:
        const std::string&  strAppName   段的名字
        const std::string&  strKeyName   属性的名字
        const int&          nDefault     默认值,如果这个属性不存在,那么返回这个值
    返回值:
        返回strKeyName对应的值,如果该strKeyName不存在,那么返回nDefault
    */
    int GetIniInt(const std::string& strAppName, const std::string& strKeyName, const int& nDefault);


    /*
    功能:从INI文件中指定位置写入一个属性,如果该属性已经存在那么这个的属性为strString
    参数:
        const std::string&  strAppName   段的名字
        const std::string&  strKeyName   属性的名字
        const std::string&  strString    strKeyName对应的的值
    返回值:
        如果成功返回true,失败返回false
    */
    bool WriteIniString(const std::string& strAppName, const std::string& strKeyName, const std::string& strString);

    /*
    功能:从INI文件中指定位置写入一个属性,如果该属性已经存在那么这个的属性为strString
    参数:
        size_t nBufferSize
        const std::string&  strAppName   段的名字
        const std::string&  strKeyName   属性的名字
        const std::string&  strString    strKeyName对应的的值
    返回值:
        如果成功返回true,失败返回false
    */
    bool WriteIniStringToNewBuffer(char* newBuffer, size_t nBufferSize, const std::string& strAppName, const std::string& strKeyName, const std::string& strString);
    /*
    功能:返回INI文件中所有段的名字,名字放在strReturnedStringList中
    参数:
        std::list&     strReturnedStringList   存放所有段的名字
    返回值:
        如果成功返回true,失败返回false
    */
    bool GetIniSection(std::list& strReturnedStringList);


    /*
    功能:返回一个段中所有属性的名字,名字放在strReturnedStringList中
    参数:
        const std::string&          strAppName              这个段的名字
        std::list&     strReturnedStringList   存放所有属性的名字
    返回值:
        如果成功返回true,失败返回false
    */
    bool GetIniSectionProperties(const std::string& strAppName, std::list& strReturnedStringList);

    /*
    功能:通过Buffer刷新程序的内部结构,函数用在通过外部更改了缓冲区内容的情形
    参数:
        无
    返回值:
        是否刷新成功
    */
    bool ReProcessIni();

    /*获取输出配置文件需要的缓冲区大小*/
    size_t BufferSize();

    /*
    功能:将配置文件输出到新的Buffer
    参数:
        char* NewBuffer 需要存放配置文件的新缓冲区
        size_t          缓冲区的大小
    返回值:
        是否刷新成功*/
    bool WriteToNewBuffer(char* NewBuffer, size_t nBufferSize);



private:
    friend class IniMemoryPrivate;
    IniMemoryPrivate* my;
    char* strIniBuffer;

private:
    //将格式化的数据写入strIniBuffer
    bool FlushBuffer(char*, size_t);
};

#endif // INIMEMORY_H

#include "IniMemory.h"
#include "IniMemoryPrivate.h"
#include <ctype>
#include <cstring>
#include <new>
#include <exception>

IniMemory::IniMemory():my(new IniMemoryPrivate(this))
{
    strIniBuffer = nullptr;
    my->listSection.clear();
}


IniMemory::IniMemory(char* strIni): my(new IniMemoryPrivate(this)), strIniBuffer(nullptr)
{
    //将最后一个字符设置为换行
    strIniBuffer = strIni;
    //初始化失败,直接异常中断处理
    if(!ReProcessIni()){
        delete my;
        my = nullptr;
        throw "初始化失败,请检查ini内容是否合法。";
    }
}

IniMemory::IniMemory(IniMemory& iniMemory): my(new IniMemoryPrivate(this)), strIniBuffer(nullptr)
{
    //完全拷贝
    this->strIniBuffer = iniMemory.strIniBuffer;
    my->listSection = iniMemory.my->listSection;
}

IniMemory::~IniMemory()
{
    if(my){
        delete my;
        my = nullptr;
    }
}


IniMemory& IniMemory::operator=(char * buffer)
{
    //如果空间不够,那么释放空间
    if(nullptr == my){
        my = new IniMemoryPrivate(this);
    }
    this->strIniBuffer = buffer;
    //初始化失败,直接异常中断处理
    if(!ReProcessIni()){
        if(strIniBuffer){
            strIniBuffer = nullptr;
        }
        my->listSection.clear();
    }
    return *this;
}

IniMemory& IniMemory::operator=(const IniMemory& iniMem)
{
    if(&iniMem == this){
        return *this;
    }
    if(nullptr == my){
        my = new IniMemoryPrivate(this);
    }
    strIniBuffer = iniMem.strIniBuffer;
    my->listSection = iniMem.my->listSection;
    return *this;
}

bool IniMemory::SetBuffer(char *strIni)
{
    this->strIniBuffer = strIni;
    if(nullptr == my){
        my = new IniMemoryPrivate(this);
    }
    //初始化失败,直接异常中断处理
    if(!ReProcessIni()){
        if(strIniBuffer){
            strIniBuffer = nullptr;
        }
        my->listSection.clear();
    }
    return true;
}

std::string IniMemory::GetIniString(const std::string &strApp, const std::string &strKey, const std::string &strDefault)
{
    if(my->listSection.empty()){
        return strDefault;
    }

    //将strAppName转换成小写
    std::string strAppName = strApp;
    std::transform(strAppName.begin(), strAppName.end(), strAppName.begin(), ::tolower);
    for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){
        if(strAppName == itIndex->strSectionName){//找到了这个段
            //继续查找属性
            std::string strKeyName = strKey;
            std::transform(strKeyName.begin(), strKeyName.end(), strKeyName.begin(), ::tolower);
            for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){
                //如果找到了这个属性,那么返回对应的值
                if(strKeyName == itSubIndex->first){
                    return itSubIndex->second;
                }
            }
        }
    }

    //如果没有找到对应的属性,那么直接返回默认值
    return strDefault;
}


int IniMemory::GetIniInt(const std::string &strApp, const std::string &strKey, const int &nDefault)
{
    if(my->listSection.empty()){
        return nDefault;
    }
    //将strAppName转换成小写
    std::string strAppName = strApp;
    std::transform(strAppName.begin(), strAppName.end(), strAppName.begin(), ::tolower);
    for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){
        if(strAppName == itIndex->strSectionName){//找到了这个段
            //继续查找属性
            std::string strKeyName = strKey;
            std::transform(strKeyName.begin(), strKeyName.end(), strKeyName.begin(), ::tolower);
            for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){
                //如果找到了这个属性,那么返回对应的值,将这个值转换成int类型返回
                if(strKeyName == itSubIndex->first){
                    return ::atoi(itSubIndex->second.c_str());
                }
            }
        }
    }
    //如果没有找到对应的属性,那么直接返回默认值
    return nDefault;
}

bool IniMemory::WriteIniString(const std::string &strApp, const std::string &strKey, const std::string &strString)
{
    //将strAppName转换成小写
    std::string strAppName = strApp;
    std::transform(strAppName.begin(), strAppName.end(), strAppName.begin(), ::tolower);
    for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){
        if(strAppName == itIndex->strSectionName){//找到了这个段
            //继续查找属性
            std::string strKeyName = strKey;
            std::transform(strKeyName.begin(), strKeyName.end(), strKeyName.begin(), ::tolower);
            for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){
                //如果找到了这个属性,那么返回对应的值,将这个值转换成int类型返回
                if(strKeyName == itSubIndex->first){
                    itSubIndex->second = strString;
                    //刷新缓冲区数据
                    return true;

                }
            }
            itIndex->listVarPair.push_back(std::make_pair(std::string(strKeyName),std::string(strString)));
            return true;
        }
    }
    //插入一个新的段
    IniMemoryPrivate::stSection section;
    section.strSectionName = strApp;
    my->listSection.push_back(section);
    my->listSection.rbegin()->listVarPair.push_back(std::make_pair(std::string(strKey),std::string(strString)));
    return true;
    //如果没有找到对应的属性,那么直接返回默认值
}


bool IniMemory::WriteIniStringToNewBuffer(char *newBuffer, size_t nBufferSize, const std::string &strApp, const std::string &strKey, const std::string &strString)
{
    //将strAppName转换成小写
    std::string strAppName = strApp;
    std::transform(strAppName.begin(), strAppName.end(), strAppName.begin(), ::tolower);
    for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){
        if(strAppName == itIndex->strSectionName){//找到了这个段
            //继续查找属性
            std::string strKeyName = strKey;
            std::transform(strKeyName.begin(), strKeyName.end(), strKeyName.begin(), ::tolower);
            for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){
                //如果找到了这个属性,那么返回对应的值,将这个值转换成int类型返回
                if(strKeyName == itSubIndex->first){
                    //保存这个值
                    std::string strTmp = itSubIndex->second;
                    //设置值
                    itSubIndex->second = strString;
                    //刷新缓冲区数据
                    bool bRes = FlushBuffer(newBuffer, nBufferSize);
                    //还原值
                    itSubIndex->second = strTmp;
                    return bRes;
                }
            }
            itIndex->listVarPair.push_back(std::make_pair(std::string(strKeyName),std::string(strString)));
            bool bRes = FlushBuffer(newBuffer, nBufferSize);
            itIndex->listVarPair.pop_back();
            return bRes;
        }
    }
    //没有找到对应的段
    IniMemoryPrivate::stSection section;
    section.strSectionName = strApp;
    my->listSection.push_back(section);
    my->listSection.rbegin()->listVarPair.push_back(std::make_pair(std::string(strKey),std::string(strString)));
    bool bRes =  FlushBuffer(newBuffer, nBufferSize);
    my->listSection.pop_back();
    return bRes;
}


bool IniMemory::GetIniSection(std::list &strReturnedString)
{
    if(my->listSection.empty()){
        return false;
    }

    for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){
        strReturnedString.push_back(itIndex->strSectionName);
    }
    return true;
}


bool IniMemory::GetIniSectionProperties(const std::string &strApp, std::list &strReturnedStringList)
{
    if(my->listSection.empty()){
        return false;
    }
    //查找这个块,找到了就将这个块里所有的变量全部放到这list里面
    //将strAppName转换成小写
    std::string strAppName = strApp;
    std::transform(strAppName.begin(), strAppName.end(), strAppName.begin(), ::tolower);
    for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){
        if(strAppName == itIndex->strSectionName){//找到了这个段
            for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){
                //将所有属性放入list中
                strReturnedStringList.push_back(itSubIndex->first);
            }
            return true;
        }
    }
    //没有找到这个块
    return false;
}


//重新解析
bool IniMemory::ReProcessIni()
{
    if(nullptr == strIniBuffer){
        return false;
    }
    my->listSection.clear();
    char tmpBuffer[4096];
    char* pEnd;
    pEnd = strIniBuffer;
    int n = 0;
    while (*pEnd || 0 != n){
        if('\n' == *pEnd || (0 != n && !(*pEnd))){
            tmpBuffer[n] = '\0';
            //获得了一行的内容,处理这一行
            //判断是不是一个空行,注释行,是的话直接跳过
            char* pBufferStart = tmpBuffer;
            char* pBufferEnd =tmpBuffer + n - 1;
            //处理前面的空格
            while (' ' == *pBufferStart || '\t' == *pBufferStart){
                pBufferStart++;
            }
            //处理末尾的空格符号
            while (' ' == *pBufferEnd || '\r' == *pBufferEnd || '\t' == *pBufferEnd){
                *pBufferEnd-- = '\0';
            }
            //如果是注释行,或者空行,那么直接跳过,处理下一行
            if(';' == *pBufferStart || 0 == strlen(pBufferStart)){
                ;
            }else if('[' == *pBufferStart){
                pBufferStart++;
                char* pos;
                //判断是不是段名
                //回这里如果找不到配对的],那么直接返false
                if((pos = strchr(pBufferStart, ']'))){
                    char* check = pos+1;
                    *pos-- = '\0';
                    //这里我再处理一下空格
                    while (' ' == *pBufferStart || '\t' == *pBufferStart){
                        pBufferStart++;
                    }
                    while (' ' == *pos || '\t' == *pos){
                        *pos-- = '\0';
                    }
                    if(0 != strlen(pBufferStart)){
                        //将段名转换成小写
                        do{
                            *pos = tolower((int)*pos);
                        }while (pos-- != pBufferStart);
                        //得到了一个段名,那么申请一个新的stSection节点,将段名放入
                        IniMemoryPrivate::stSection section;
                        section.strSectionName = pBufferStart;
                        my->listSection.push_back(section);
                    }
                    //检查是不是合法的段名
                    while('\0' != *check){
                        if(';' == *check){
                            break;
                        }
                        if(' ' != *check && '\t' != *check){
                            my->listSection.clear();
                            strIniBuffer = nullptr;
                            return false;
                        }
                        check++;
                    }
                }else{
                    strIniBuffer = nullptr;
                    return false;
                }
            }else{
                //否则判断这一行是不是属性
                char* pos = nullptr;
                if((pos = strchr(pBufferStart, '='))){
                    //如果是一个变量,那么将该变量放入最后一个段的那个节点的list对中
                    char* pVarEnd = pos;
                    pVarEnd--;
                    //这里只需要把后面的空格去掉,因为前面的空格已经在最开始清除掉了
                    while (' ' == *pVarEnd || '\t' == *pVarEnd){
                        *pVarEnd-- = '\0';
                    }
                    //将变量名转换成小写
                    while (pVarEnd != pBufferStart){
                        *pVarEnd = ::tolower(*pVarEnd);
                        pVarEnd--;
                    }
                    *pVarEnd = ::tolower(*pVarEnd);

                    *pos++ = '\0';
                    while (' ' == *pos || '\t' == *pos){
                        pos++;
                    }
                    char* pSubPos = nullptr;
                    //注释忽略
                    if((pSubPos = strchr(pos, ';'))){
                        *pSubPos-- = '\0';
                    }
                    while (pSubPos && (' ' == *pSubPos || '\t' == *pSubPos)){
                        *pSubPos-- = '\0';
                    }

                    //设置如果变量名不空,那么放入
                    if(0 != strlen(pBufferStart)){
                        //全局的属性
                        if(0 == my->listSection.size()){
                            IniMemoryPrivate::stSection section;
                            section.strSectionName = "";
                            my->listSection.push_back(section);
                        }
                        //将该对值放入list中
                        my->listSection.rbegin()->listVarPair.push_back(std::make_pair(pBufferStart,pos));
                    }
                }else{//说明不是段名,不是属性,也不是注释,那么要他干嘛,终止解析,game over
                    strIniBuffer = nullptr;
                    return false;
                }
            }
            n = 0;
            if('\n' == *pEnd){
                pEnd++;
            }
            continue;
        }else{
            tmpBuffer[n] = *pEnd;
        }
        n++;
        pEnd++;
    }
    strIniBuffer = nullptr;
    return true;
}

size_t IniMemory::BufferSize()
{
    //返回Buffer需要的大小
    return my->CountNeedBuffer();
}

//将所有内容输出到新的缓冲区中
bool IniMemory::WriteToNewBuffer(char* NewBuffer, size_t nBufferSize)
{
    return FlushBuffer(NewBuffer, nBufferSize);
}



//输出到新的缓冲区
bool IniMemory::FlushBuffer(char * newBuffer, size_t nBufferSize)
{
    if(nullptr == newBuffer){
        return false;
    }
    size_t Buffersize = my->CountNeedBuffer();
    if(nBufferSize < Buffersize){
        return false;
    }
    char* pBufferEnd = newBuffer;
    //不去管内存大小够不够,这个问题留给外面去考虑,我只管复制
    for (auto itIndex = my->listSection.begin(); itIndex != my->listSection.end(); itIndex++){
        if(itIndex != my->listSection.begin() || "" != itIndex ->strSectionName){
            *pBufferEnd++ = '[';
            strcpy(pBufferEnd, itIndex->strSectionName.c_str());
            pBufferEnd += strlen(pBufferEnd);
            *pBufferEnd++ = ']';
            *pBufferEnd++ = '\n';
        }
        //添加本段中的所有属性到配置文件中
        for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){
            //把变量名复制到相应位置
            strcpy(pBufferEnd, itSubIndex->first.c_str());
            pBufferEnd += strlen(pBufferEnd);
            *pBufferEnd++ = '=';
            strcpy(pBufferEnd, itSubIndex->second.c_str());
            pBufferEnd += strlen(pBufferEnd);
            *pBufferEnd++ = '\n';
        }
    }
    *pBufferEnd = '\0';
    return true;
}
#ifndef INIMEMORYPRIVATE_H
#define INIMEMORYPRIVATE_H

//list
#include <list>
//make_pair
#include <utility>
//常用算法
#include <algorithm>
//std::string
#include <string>
#include "IniMemory.h"

class IniMemory;

class IniMemoryPrivate
{
public:
    IniMemoryPrivate(IniMemory* my);

private:
    typedef struct {
        std::string strSectionName;
        std::list> listVarPair;
    }stSection;
    friend class IniMemory;
    IniMemory* my;
    std::list listSection;
    size_t CountNeedBuffer();
};

#endif // INIMEMORYPRIVATE_H
#include "IniMemory.h"
#include "IniMemoryPrivate.h"


IniMemoryPrivate::IniMemoryPrivate(IniMemory * my): my(my)
{

}
size_t IniMemoryPrivate::CountNeedBuffer()
{
    size_t Buffersize = 0;
    for (auto itIndex = listSection.begin(); itIndex != listSection.end(); itIndex++){
        if(itIndex != listSection.begin() || "" != itIndex->strSectionName){
            Buffersize += itIndex ->strSectionName.length();
            Buffersize += 3;
        }
        for (auto itSubIndex = itIndex->listVarPair.begin(); itSubIndex != itIndex->listVarPair.end(); itSubIndex++){
            //把变量名复制到相应位置
            Buffersize += itSubIndex->first.length();
            Buffersize += itSubIndex->second.length();
            Buffersize += 2;
        }
    }
    Buffersize++;//存放最后的‘\0’
    return Buffersize;
}

程序是用C++写的,用一个私有类隐藏了类成员。这样可以只发布一个二进制和一个头文件就可以了。用到了一点C++11的特性,VC2010以下的需要改一下有一个位置模板的>>和for循环中的auto,其他的不用动。
2017-09-14
暂无评论

发表回复