zoukankan      html  css  js  c++  java
  • 纯C/C++封装的INI文件读写类

    // CIniHelper.h Copyallright By DJH5520 2017-10
    #ifndef _CINIHELPER_H_
    #define _CINIHELPER_H_ #include <unordered_map> // 查找速度O(1) #include <vector> #include <string> #include <fstream> #include <cassert> #include <exception> #include <iterator> #include <algorithm> #include <functional> #include <cstdio> // 键值对结构 struct SKeyValue { std::string m_strKey; std::string m_strValue; }; // Section结构 struct SSection { std::string m_strSectionName; // Section名称 std::vector<SKeyValue> m_kvPairs;// 当前Section下所有键值对,按顺序存储 }; class CIniHelper { private: // 禁止外部构造 CIniHelper() = default; // 禁止拷贝 CIniHelper(const CIniHelper& other) = delete; CIniHelper& operator=(const CIniHelper& other) = delete; ~CIniHelper() { DumpToFile(); } public: /*! * 功能:获取单例对象指针(注:直接采用饿汉模式以保证线程安全) * 参数: 无 * 返回值: 内部创建的单例对象指针 */ static CIniHelper* GetInstance(); /*! * 功能:释放单例对象指针(在任何地方均不需要使用本类时,调用该函数delete创建的单例对象,否则只在程序结束时释放) * 参数: 无 * 返回值: void */ static void FreeInstance(); /*! * 功能:加载ini文件,使用前必须先加载ini * 参数: strPath:ini文件的路径 * 返回值: void */ void LoadIniFile(const std::string& strPath); /*! * 功能:将当前ini内容写入文件 * 参数: void * 返回值: 成功返回0, 失败返回-1 */ int DumpToFile() { return WriteFile(m_strFilePath); } //==================================读写方法,模拟Windows API函数格式=================================// /*! * 功能:按int类型获取指定Section下指定Key的Value值 * 参数: strSecName:指定查找的节点名称 strKeyName:指定查找的Key nDefault:当未找到时,返回的默认值 * 返回值: 返回找到的value值 */ int GetPrivateProfileInt(const std::string& strSecName, const std::string& strKeyName, int nDefault); /*! * 功能:按double类型获取指定Section下指定Key的Value值 * 参数: strSecName:指定查找的节点名称 strKeyName:指定查找的Key dDefault:当未找到时,返回的默认值 * 返回值: 返回找到的value值 */ double GetPrivateProfileDouble(const std::string& strSecName, const std::string& strKeyName, double dDefault); /*! * 功能:按string类型获取指定Section下指定Key的Value值 * 参数: strSecName:指定查找的节点名称 strKeyName:指定查找的Key strDefault:当未找到时,返回的默认值 * 返回值: 返回找到的value值 */ std::string GetPrivateProfileString(const std::string& strSecName, const std::string& strKeyName, const std::string& strDefault); /*! * 功能:获取指定Section下所有的键值对 * 参数: strSecName:指定查找的节点名称 kvPairs:输出参数,用于保存获取到的键值对 * 返回值: 获取成功返回0,失败返回-1 */ int GetPrivateProfileSection(const std::string& strSecName, std::vector<SKeyValue>& kvPairs); /*! * 功能:获取ini文件中所有Section的名称 * 参数: vecSecNames:输出参数,用于保存获取到的名称 * 返回值: 无 */ void GetPrivateProfileSectionNames(std::vector<std::string>& vecSecNames); /*! * 功能:写入或修改指定Section下指定Key的Value值(不存在则写入,已存在则修改value值) * 参数: strSecName:写入或修改的节点名称 strKeyName:写入或修改的键名 strValue:写入或修改的键的值 * 返回值: 成功返回0,失败返回-1 */ int WritePrivateProfileString(const std::string& strSecName, const std::string& strKeyName, const std::string& strValue); private: /// 按行读取文件内容 void ReadFile(const std::string& strPath); /// 按行写入文件 int WriteFile(const std::string& strPath); /// 去除字符串首尾空白 void trim(std::string& str); /// 去除字符串左边空白 void TrimLeft(std::string& str); /// 去除字符串右边空白 void TrimRight(std::string& str); /// 忽略大小写比较字符串 int CompareNocase(const std::string& str1, const std::string& str2); /// 判断是否是Section inline bool IsSection(const std::string& str); /// 判断一行字符串是否是键值对 bool IsKeyValuePairs(const std::string& str); /// 获取所有的Section void GetAllSections(); /// 从键值对行提取键值对,使用前提:调用之前已经判断该行就是键值对 bool GetKeyValuePair(const std::string& strLine, std::string& strKey, std::string& strValue); #if 0 bool GetKeyValue(const std::string& strLine, SKeyValue& kvPair); #endif /// 根据Section和Key查找Value,未找到则返回空串 std::string GetValueString(const std::string& strSecName, const std::string& strKeyName); /// 获取最第一个注释符号的位置(支持注释符号:"//" "#" ";") size_t GetCommentPos(const std::string& strLine); private: /// ini文件路径 std::string m_strFilePath; /// ini文件内容 std::vector<std::string> m_fileContent; #if 0 /// 所有Section,选用vector容器以保留原文顺序,追求效率可以使用map/hash这种容器 std::vector<SSection> m_sections; #endif /// 单例对象指针 static CIniHelper* m_instance; /// 核心成员:<Section, <Key, Value>> unordered_map缺点是无法保持原文顺序,只是查询效率高,需要保持顺序建议用顺序性容器 std::unordered_map<std::string, std::unordered_map<std::string, std::string>> m_allSection; }; #endif
    #include "CIniHelper.h"
    
    
    // 单例对象初始化
    CIniHelper* CIniHelper::m_instance = new CIniHelper;
    
    CIniHelper* CIniHelper::GetInstance()
    {
        return m_instance;
    }
    
    void CIniHelper::FreeInstance()
    {
        if (m_instance)
        {
            delete m_instance;
            m_instance = nullptr;
        }
    }
    
    void CIniHelper::LoadIniFile(const std::string& strPath)
    {
        // 清空所有容器
        // 关于vector容器:
        // clear方法并不会真正释放已经分配的内存,查看其源码可知它只是调用内部元素的析构函数,已分配的内存并不会释放,可以调用capacity查看其容量并未改变
        // 这是vector的内存优化策略,当再次使用该容器时,它只需要调用构造函数对已有的内存初始化即可,vector真正释放内存是在其自身析构时
        // 使用swap将其与一个空的临时vector对象交换,可以将旧的内存转移到临时vector,当临时对象析构时旧的内存就释放了
        //m_fileContent.clear();
        m_fileContent.swap(std::vector<std::string>());
    
        // map容器的erase和clear时会释放内存的(根据STL源码剖析)
        m_allSection.clear();
    
        // 读取ini文件内容到容器
        assert(strPath != "");
        m_strFilePath = strPath;
        ReadFile(m_strFilePath);
    
        // 解析ini中所有Section
        GetAllSections();
    }
    
    int CIniHelper::GetPrivateProfileInt(const std::string& strSecName, const std::string& strKeyName, int nDefault)
    {
        std::string strValue = GetValueString(strSecName, strKeyName);
    
        if (strValue.empty())
        {
            return nDefault;
        }
    
        return std::stoi(strValue);
    }
    
    
    double CIniHelper::GetPrivateProfileDouble(const std::string& strSecName, const std::string& strKeyName, double dDefault)
    {
        std::string strValue = GetValueString(strSecName, strKeyName);
    
        if (strValue.empty())
        {
            return dDefault;
        }
    
        return std::stod(strValue);
    }
    
    
    std::string CIniHelper::GetPrivateProfileString(const std::string& strSecName,
        const std::string& strKeyName,
        const std::string& strDefault)
    {
        std::string strValue = GetValueString(strSecName, strKeyName);
    
        if (strValue.empty())
        {
            return strDefault;
        }
        else
        {
            return strValue;
        }
    }
    
    
    int CIniHelper::GetPrivateProfileSection(const std::string& strSecName, std::vector<SKeyValue>& kvPairs)
    {
        try
        {
            kvPairs.clear();
    
            // 查找Section下所有的键值对一般需要保持顺序,unordered_map没法用,只能用顺序容器或去原文查找
            std::string strInnerSec = "[" + strSecName + "]";
            auto it = m_fileContent.cbegin();
            for (; it != m_fileContent.cend(); ++it)
            {
                if (IsSection(*it) && (*it) == strInnerSec)
                    break; // 找到了
            }
    
            if (it != m_fileContent.cend())
            {
                // 继续提取该Section下面,直至下一个Section之间的KeyValue
                ++it;
                for (; it != m_fileContent.cend(); ++it)
                {
                    if (IsSection(*it))
                    {
                        break; // 已到达下一个Section
                    }
                    else if (IsKeyValuePairs(*it))
                    {
                        SKeyValue kv;
                        if (GetKeyValuePair(*it, kv.m_strKey, kv.m_strValue))
                        {
                            kvPairs.emplace_back(kv);
                        }
                    }
                }
            }
        }
        catch (const std::exception&)
        {
            return -1;
        }
    
    
    #if 0
        std::string strInnerSec = "[" + strSecName + "]";
        for (auto iter = m_sections.cbegin(); iter != m_sections.cend(); ++iter)
        {
            auto& sec = *iter;
            if (sec.m_strSectionName == strInnerSec)
            {
                kvPairs = sec.m_kvPairs;
                return 0;
            }
        }
    #endif
    
        return 0;
    }
    
    
    void CIniHelper::GetPrivateProfileSectionNames(std::vector<std::string>& vecSecNames)
    {
        vecSecNames.clear();
    
        // 获取Section一般需要保持顺序,unordered_map没法用,只能用顺序容器原文查找
        for (auto& strLine : m_fileContent)
        {
            if (IsSection(strLine))
            {
                std::string strSec = strLine.substr(1, strLine.length() - 2); // 去除"[" "]"即第一个和最后一个
                trim(strSec); // 去除空白
                vecSecNames.emplace_back(strSec);
            }
        }
    
    
    #if 0
        for (auto it = m_sections.cbegin(); it != m_sections.cend(); ++it)
        {
            vecSecNames.push_back(it->m_strSectionName);
        }
    #endif
    }
    
    int CIniHelper::WritePrivateProfileString(const std::string& strSecName, const std::string& strKeyName, const std::string& strValue)
    {
        try
        {
            // 写入Section的键值对一般需要保持顺序,unordered_map没法用,只能用顺序容器原文查找
            std::string strInnerSec = "[" + strSecName + "]";
    
            // 查找Section
            auto it = m_fileContent.begin();
            for (; it != m_fileContent.end(); ++it)
            {
                if (IsSection(*it) && (*it) == strInnerSec)
                    break;// 找到了指定的Section
            }
    
            bool bFindKey = false;
            if (it != m_fileContent.end())
            {// 找到了指定的Section,继续查找Section下面是否存在指定的Key
                ++it; // 跳到下一行
                for (; it != m_fileContent.end(); ++it)
                {
                    // 是否是键值对
                    if (IsKeyValuePairs(*it))
                    {
                        if ((*it).find(strKeyName) != std::string::npos)
                        {// 找到了Key
                            bFindKey = true;
    
                            // 获取该Key原来的值
                            std::string strOldValue = GetPrivateProfileString(strSecName, strKeyName, "");
    
                            // 如果一致则不用修改
                            if (strOldValue == strValue) return 0;
    
                            // 替换旧的value
                            if (strOldValue == "")
                            {// 原来的Value为空
                                (*it) = strKeyName + "=" + strValue;
                            }
                            else
                            {
                                size_t oldValuePos = (*it).find(strOldValue);
                                if (oldValuePos != std::string::npos)
                                {
                                    (*it).replace(oldValuePos, strOldValue.length(), strValue); // 替换
                                }
                                else
                                {// 没找到旧的值,直接修改为新的值
                                    (*it) = strKeyName + "=" + strValue;
                                }
                            }
    
                            // 同步更新
                            m_allSection[strInnerSec][strKeyName] = strValue;
                            break; 
                        }
                        continue;
                    }
                    else if ((bFindKey == false) && ((it + 1) == m_fileContent.end()) && IsSection(*(it + 1)))
                    {// 提前检查下一行是不是新的Section,或者已经是最后一行, 如果是 且未找到Key,则证明这是一个新的Key,则将其插入当前Section的最后
    
                        // 插入这个新的key, insert函数是在指定位置前一个位置插入,所以要加+才表示在当前位置后面插入
                        std::string strPair = strKeyName + "=" + strValue;
                        m_fileContent.insert(it + 1, strPair);
                        // 同步更新
                        m_allSection[strInnerSec][strKeyName] = strValue;
                        break;
                    }
                }
            }
            else
            {// 未找到指定Section,则这是一个新的Section,将其添加到文件最后
                m_fileContent.push_back(strInnerSec);
                m_fileContent.push_back(strKeyName + "=" + strValue);
    
                // 同步更新
                std::unordered_map<std::string, std::string> kv;
                kv.emplace(strKeyName, strValue);
                m_allSection.emplace(strInnerSec, kv);
            }
    
            // 每次修改都保存文件效率不高,可以考虑使用DumpToFile接口,让用户不再修改时再真正保存到文件,或者析构时写入
            // return WriteFile(m_strFilePath); 
            return 0;
        }
        catch (const std::exception&)
        {
            return -1;
        }
    }
    
    //===============================================================================================//
    void CIniHelper::ReadFile(const std::string& strPath)
    {
        try
        {
            std::ifstream fs;
            fs.open(strPath); 
            std::string strLine;
            // 按行读取文本文件
            while (fs)
            {
                std::getline(fs, strLine);
                m_fileContent.emplace_back(strLine); // 效率比push_back高
            }
            fs.close();
        }
        catch (const std::exception&)
        {
            std::abort();
        }
    }
    
    
    int CIniHelper::WriteFile(const std::string& strPath)
    {
        std::string strTempFilePath =  strPath + ".tmp";
    
        try
        {
            std::ofstream fs;
            // 先将内容写入临时文件,写入成功后替换原文件,防止写入失败时破坏原文件
            fs.open(strTempFilePath);
            auto it = m_fileContent.cbegin();
            for (; it != (m_fileContent.cend() - 1); ++it)
            {
                fs << *it << "
    ";
            }
            // 最后一行不加换行符
            fs << *it;
    
            fs.close();
        }
        catch (const std::exception&)
        {
            return -1;
        }
    
        std::remove(strPath.c_str()); // 删除原文件
        std::rename(strTempFilePath.c_str(), strPath.c_str()); // 将临时文件重命名
    
        return 0;
    }
    
    
    void CIniHelper::trim(std::string& str)
    {
        if (!str.empty())
        {
            const char strSpace[] = " 	
    
    fv"; // 空白字符集
            str.erase(0, str.find_first_not_of(strSpace));
            str.erase(str.find_last_not_of(strSpace) + 1);
        }
    }
    
    void CIniHelper::TrimLeft(std::string& str)
    {
        if (!str.empty())
        {
            const char strSpace[] = " 	
    
    fv"; // 空白字符
            str.erase(0, str.find_first_not_of(strSpace));
        }
    }
    
    void CIniHelper::TrimRight(std::string& str)
    {
        if (!str.empty())
        {
            const char strSpace[] = " 	
    
    fv"; // 空白字符
            str.erase(str.find_last_not_of(strSpace) + 1);
        }
    }
    
    int CIniHelper::CompareNocase(const std::string& str1, const std::string& str2)
    {
        std::string s1, s2;
    
        // 均转换成小写后比较
        std::transform(str1.cbegin(), str1.cend(), back_inserter(s1), ::tolower);
        std::transform(str2.cbegin(), str2.cend(), back_inserter(s2), ::tolower);
    
        return s1.compare(s2);
    }
    
    bool CIniHelper::IsSection(const std::string& str)
    {
        size_t len = str.length();
        // 一个合法的Section长度最少为3,且第一个和最后一个字符分别是'['和']', 如:[Config]
        if (len >= 3 && str.at(0) == '[' && str.at(len - 1) == ']')
        {
            return true;
        }
        else
            return false;
    }
    
    bool CIniHelper::IsKeyValuePairs(const std::string& str)
    {
        if (str.length() >= 3 && str.find('=') != std::string::npos)
        {
            return true;
        }
        return false;
    }
    
    void CIniHelper::GetAllSections()
    {
        // 从容器中获取文件中的每一行文本,并解析
        for (auto& strLine : m_fileContent)
        {
            static std::string SectionName = ""; // 静态变量,用于记住键值对上面的Section名
    
            // 去除首位空白字符
            trim(strLine);
    
            // 判断文本内容是否是Section还是Section下的键值对
            if (IsSection(strLine))
            {// Section    
                SectionName = strLine;// 记住Section名,如果该Section存在键值对,则下一行文本的内容就是其键值对
                m_allSection.emplace(SectionName, std::unordered_map<std::string, std::string>()); // Section对应的键值对容器暂时为空
                continue;
            }
            else if (IsKeyValuePairs(strLine))
            {// 键值对
                // 提取Key和Value
                std::string strKey, strValue;
                if (GetKeyValuePair(strLine, strKey, strValue))
                {
                    // 将其插入对应的Section下面
                    if (!SectionName.empty())
                    {
                        auto& kvMap = m_allSection.at(SectionName);
                        kvMap.emplace(strKey, strValue);
                    }
                }
            }
        }
    
    #if 0
        // 解析文件每一行,获取键值对
        for (auto iter = m_fileContent.cbegin(); iter != m_fileContent.cend(); ++iter)
        {
            std::string strLine = *iter;
    
            // 删除一行的前后空白
            trim(strLine);
    
            // 判断类型
            if (IsSection(strLine))
            {
                // 先尾部插入一个Section
                SSection tmp;
                tmp.m_strSectionName = strLine;
                m_sections.push_back(tmp);
            }
            else if (IsKeyValuePairs(strLine))
            {
                // 如果是键值对,则需要将其放到它对应的Section下面(最后一个插入的那个Section就是,它上面的Section)
                SSection& sec = m_sections.back();
                // 提取Key和Value
                SKeyValue tmp;
                if (GetKeyValue(strLine, tmp))
                {
                    sec.m_kvPairs.push_back(tmp);
                }
            }
        }
    #endif
    }
    
    bool CIniHelper::GetKeyValuePair(const std::string& strLine, std::string& strKey, std::string& strValue)
    {
        strKey = "", strValue = "";
        // 提取Key(不能为空)
        size_t posEqual = strLine.find('='); // 找到第一个'='位置
        if (posEqual == 0) return false; // 如果第1个字符就是'='则不是键值对
        strKey = strLine.substr(0, posEqual); // 截取"="前面的字符串作为Key
        // 去除空白
        trim(strKey);
        if (strKey.empty()) return false;
    
        // 提取value(可以为空)
        // 获取第一个注释符号的位置,目前支持的注释符有 "//" '#' ';'
        size_t posComment = GetCommentPos(strLine);
        if ((posComment > posEqual + 1) && (posComment != std::string::npos)) // 检查注释符位置是否合法
        {
            // 有注释符号,截取"="后面到注释符号之间的内容作为Value
            strValue = strLine.substr(posEqual + 1, posComment - posEqual - 1);
        }
        else
        {
            // 没有注释则直接截取=后面的内容
            strValue = strLine.substr(posEqual + 1);
        }
        trim(strValue);
    
        return true;
    }
    
    #if 0
    bool CIniHelper::GetKeyValue(const std::string& strLine, SKeyValue& kvPair)
    {
        // 提取Key
        size_t posEqual = strLine.find('='); // 找到第一个'='位置
        if (posEqual == 0) return false; // 如果第一个字符就是'='则非法
        std::string strKey = strLine.substr(0, posEqual);
        // 去除左右空白
        trim(strKey);
        if (strKey.empty()) return false;
    
        // 提取value
        std::string strValue = "";
        // 获取第一个注释符号的位置
        size_t posComment = GetCommentPos(strLine);
        if ((posComment > posEqual + 1) && (posComment != std::string::npos)) // 检查位置是否合法
        {
            // 有注释符号,截取=后面到注释符号之间的内容
            strValue = strLine.substr(posEqual + 1, posComment - posEqual - 1);
        }
        else
        {
            // 没有注释 截取=后面的内容
            strValue = strLine.substr(posEqual + 1);
        }
        // 去除空白
        trim(strValue);
    
        kvPair.m_strKey = strKey;
        kvPair.m_strValue = strValue;
    
        return true;
    }
    #endif
    
    std::string CIniHelper::GetValueString(const std::string& strSecName, const std::string& strKeyName)
    {
        std::string strResult = "";
        std::string strInterSec = "[" + strSecName + "]"; // eg: [Config]
    
        // 查找对应Section
        auto sec = m_allSection.find(strInterSec);
        if (sec != m_allSection.end())
        {
            // 存在则继续查找Key
            auto& kvPair = sec->second;
    
            auto it = kvPair.find(strKeyName);
            if (it != kvPair.end())
            {
                // 找到了
                strResult = it->second;
            }
        }
    
    #if 0
        std::string strInterSec = "[" + strSecName + "]";
    
        // 查找Section
        for (auto iter = m_sections.cbegin(); iter != m_sections.cend(); ++iter)
        {
            auto& sec = *iter;
    
            // 找到了Section
            if (sec.m_strSectionName == strInterSec)
            {
                // 继续查找Key
                const std::vector<SKeyValue>& kvs = sec.m_kvPairs;
                for (auto it = kvs.cbegin(); it != kvs.cend(); ++it)
                {
                    // 找到了Key
                    if (strKeyName == (*it).m_strKey)
                    {
                        strResult = (*it).m_strValue;
                        break;
                    }
                }
    
                break;
            }
        }
    #endif
    
        return strResult;
    }
    
    size_t CIniHelper::GetCommentPos(const std::string& strLine)
    {
        size_t spos[3] = { 0 }; // pos[0]表示"//"第一次出现的位置,pos[1]表示"#",pos[2]表示";"第一次出现的位置
        spos[0] = strLine.find("//");
        spos[1] = strLine.find("#");
        spos[2] = strLine.find(";");
    
        // 取最小值,即最先出现的注释符位置
        size_t* pos = std::min_element(std::begin(spos), std::end(spos));
        return *pos;
    }
    
    //=======================================================================================//
    #include "CIniHelper.h"
    #include <cstdio>
    #include <cstdlib>
    #include <iostream>
    #include <ctime>
    
    clock_t g_start = 0, g_end = 0;
    
    int main(int argc, char* argv[]) 
    {
    #if 1
        g_start = std::clock();
        CIniHelper* pini = CIniHelper::GetInstance();
        g_end = std::clock();
        std::cout << ">>>>>>>>>>>>>>GetInstance() Use Time:" << g_end - g_start << "ms
    "<< std::endl;
    
    
        // 首次使用时,必须加载ini文件,最耗时的函数
        g_start = std::clock();
        pini->LoadIniFile("./Config.ini");
        g_end = std::clock();
        std::cout << ">>>>>>>>>>>>>>LoadIniFile() Use Time:" << g_end - g_start << "ms
    " << std::endl;
    
    
        // 获取int值
        g_start = std::clock();
        std::cout << "[PN9Test]DummyFrame=" << pini->GetPrivateProfileInt("PN9Test", "DummyFrame", 0) << std::endl;
        g_end = std::clock();
        std::cout << ">>>>>>>>>>>>>>GetPrivateProfileInt() Use Time:" << g_end - g_start << "ms
    " << std::endl;
    
    
        // 获取double类型
        g_start = std::clock();
        std::cout << "[Capture_D50]DFOV=" << pini->GetPrivateProfileDouble("Capture_D50", "DFOV", 0) << std::endl;
        g_end = std::clock();
        std::cout << ">>>>>>>>>>>>>>GetPrivateProfileDouble() Use Time:" << g_end - g_start << "ms
    " << std::endl;
    
        // 获取string
        g_start = std::clock();
        std::cout << "[TestItems]LightDefectPixel=" << pini->GetPrivateProfileString("TestItems", "LightDefectPixel", "") << std::endl;
        g_end = std::clock();
        std::cout << ">>>>>>>>>>>>>>GetPrivateProfileString() Use Time:" << g_end - g_start << "ms
    " << std::endl;
    
        // 获取整个Section下所有键值对
        std::vector<SKeyValue> kvPairs;
        g_start = std::clock();
        pini->GetPrivateProfileSection("TestItems", kvPairs);
        g_end = std::clock();
        std::cout << "[Section TestItems]" << std::endl;
        for (auto it = kvPairs.begin(); it != kvPairs.end(); ++it)
        {
            std::cout << it->m_strKey << "=" << it->m_strValue << std::endl;
        }
        std::cout << ">>>>>>>>>>>>>>GetPrivateProfileSection() Use Time:" << g_end - g_start << "ms
    " << std::endl;
    
        // 回写文件
        g_start = std::clock();
        pini->WritePrivateProfileString("Capture_SFR145", "Test_Ver", "10.2.8");
        g_end = std::clock();
        std::cout << ">>>>>>>>>>>>>>WritePrivateProfileString() Use Time:" << g_end - g_start << "ms
    " << std::endl;
    
        CIniHelper::FreeInstance();
    #endif
        system("pause");
        return 0;
    }
  • 相关阅读:
    MySQL乱码问题
    Oracle安装错误“程序异常终止
    Hibernate Spring
    快学Scala 第一课 (变量,类型,操作符)
    Scala 学习笔记之隐式参数和隐式转换并用
    Hadoop 学习笔记之 Incompatible namespaceIDs问题
    Kafka 学习笔记之 Topic日志清理
    Zookeeper 学习笔记之 节点个数
    Kafka 学习笔记之 删除Topic
    Kafka 学习笔记之 架构
  • 原文地址:https://www.cnblogs.com/djh5520/p/14521940.html
Copyright © 2011-2022 走看看