【问题标题】:What is the easiest way to parse an INI File in C++? [closed]在 C++ 中解析 INI 文件的最简单方法是什么? [关闭]
【发布时间】:2010-09-05 23:20:36
【问题描述】:

我正在尝试使用 C++ 解析 INI 文件。关于实现这一目标的最佳方法的任何提示?我应该使用 Windows API 工具来处理 INI 文件(我完全不熟悉)、开源解决方案还是尝试手动解析它?

【问题讨论】:

    标签: c++ winapi ini fileparse


    【解决方案1】:

    我从来没有解析过 ini 文件,所以我不能对这个问题说得太具体。
    但我有一个建议:
    只要现有的满足您的要求,就不要重新发明轮子

    http://en.wikipedia.org/wiki/INI_file#Accessing_INI_files
    http://sdl-cfg.sourceforge.net/
    http://sourceforge.net/projects/libini/
    http://www.codeproject.com/KB/files/config-file-parser.aspx

    祝你好运:)

    【讨论】:

      【解决方案2】:

      您可以使用 Windows API 函数,例如 GetPrivateProfileString()GetPrivateProfileInt()

      【讨论】:

      • GetPrivateProfileInt() 和其他函数不被 MSDN 推荐,因为它们已经过时并且仍然只提供与较旧的 16 位系统的基本兼容性。而不是使用其他方法。 msdn.microsoft.com/en-us/library/windows/desktop/…
      • 它们已经过时了,因为 MS 不希望你再使用 ini 文件了,如果你真的想读或写这样的文件,它们仍然是理想的。
      【解决方案3】:

      除非您打算使应用程序跨平台,否则使用 Windows API 调用将是最好的方法。只需忽略 API 文档中关于仅为 16 位应用程序兼容性提供的说明。

      【讨论】:

        【解决方案4】:

        如果您需要跨平台解决方案,请尝试 Boost 的 Program Options 库。

        【讨论】:

        • 这是要走的路,我不明白为什么人们只是投票赞成不那么普遍的答案。
        • @Gollum,听起来 Windows 是一个给定的依赖项。使用 Program Options 库意味着承担另一个依赖项。有时这没什么大不了的,有时是。
        • Boost 的程序选项似乎使用了它自己的配置格式,井号 (#) 用于 cmets(而不是分号)boost.org/doc/libs/1_57_0/doc/html/program_options/…
        • @malat 我很困惑,我没有提到投票反对?
        • 他正在尝试读取现有的 INI 文件,使用 boost 不是答案,因为它使用的是类似 INI 的格式。
        【解决方案5】:

        我使用SimpleIni。它是跨平台的。

        【讨论】:

        • SimpleIni 现在托管在 Github 上。
        • 它似乎支持 Visual Studio 和 GCC,MIT 许可证。截至撰写本文时,其自动构建失败,最后一次发布是在 2013 年 9 月,最后一次提交:2020 年 6 月
        【解决方案6】:

        你试过libconfig;非常类似于 JSON 的语法。我更喜欢它而不是 XML 配置文件。

        【讨论】:

          【解决方案7】:

          这个问题有点老了,但我会发布我的答案。我已经测试了各种 INI 类(您可以在我的 website 上看到它们)并且我还使用 simpleIni,因为我想在 windows 和 winCE 上使用 INI 文件。 Window 的 GetPrivateProfileString() 仅适用于 winCE 上的注册表。

          使用 simpleIni 很容易阅读。这是一个例子:

          #include "SimpleIni\SimpleIni.h"    
          CSimpleIniA ini;
          ini.SetUnicode();
          ini.LoadFile(FileName);
          const char * pVal = ini.GetValue(section, entry, DefaultStr);
          

          【讨论】:

            【解决方案8】:

            如果你已经在使用 Qt

            QSettings my_settings("filename.ini", QSettings::IniFormat);
            

            然后读取一个值

            my_settings.value("GroupName/ValueName", <<DEFAULT_VAL>>).toInt()
            

            还有许多其他转换器可以将您的 INI 值转换为标准类型和 Qt 类型。有关详细信息,请参阅 QSettings 上的 Qt 文档。

            【讨论】:

            • 不错,尽管如果您进行更改,它们会将它们保存回 .ini 文件,而不会真正告诉您(即析构函数调用sync(),这可能会令人惊讶),这会破坏 cmets 和之前定义变量的顺序...
            【解决方案9】:

            如果你对平台可移植性感兴趣,你也可以试试 Boost.PropertyTree。它支持 ini 作为持久性格式,尽管属性树只有 1 级深。

            【讨论】:

              【解决方案10】:

              inih 是一个用 C 编写的简单 ini 解析器,它也带有一个 C++ 包装器。示例用法:

              #include "INIReader.h"    
              
              INIReader reader("test.ini");
              
              std::cout << "version="
                        << reader.GetInteger("protocol", "version", -1) << ", name="
                        << reader.Get("user", "name", "UNKNOWN") << ", active="
                        << reader.GetBoolean("user", "active", true) << "\n";
              

              作者还有一个现有库here的列表。

              【讨论】:

                【解决方案11】:

                也许是一个迟到的答案..但是,值得了解的选项..如果您需要跨平台解决方案,绝对可以尝试 GLIB,它很有趣.. (https://developer.gnome.org/glib/stable/glib-Key-value-file-parser.html)

                【讨论】:

                  【解决方案12】:

                  我知道这个问题很老了,但是我遇到它是因为我需要一些跨平台的linux,win32...我写了下面的函数,它是一个可以解析INI文件的函数,希望其他人能找到很有用。

                  规则和注意事项: 要解析的 buf 必须是一个以 NULL 结尾的字符串。将您的 ini 文件加载到 char 数组字符串中并调用此函数来解析它。 部分名称必须有 [] 括号,例如 [MySection],值和部分也必须从没有前导空格的行开始。它将解析带有 Windows \r\n 或 Linux \n 行结尾的文件。注释应使用 # 或 // 并从文件顶部开始,任何 cmets 不应与 INI 条目数据混合。从返回字符串的两端修剪引号和刻度。只有在引号之外的空格才会被修剪。字符串不需要有引号,如果缺少引号,空格会被修剪。您还可以提取数字或其他数据,例如,如果您有一个浮点数,只需在 ret 缓冲区上执行 atof(ret)。

                  //  -----note: no escape is nessesary for inner quotes or ticks-----
                  //  -----------------------------example----------------------------
                  //  [Entry2]
                  //  Alignment   = 1
                  //  LightLvl=128
                  //  Library     = 5555
                  //  StrValA =  Inner "quoted" or 'quoted' strings are ok to use
                  //  StrValB =  "This a "quoted" or 'quoted' String Value"
                  //  StrValC =  'This a "tick" or 'tick' String Value'
                  //  StrValD =  "Missing quote at end will still work
                  //  StrValE =  This is another "quote" example
                  //  StrValF =  "  Spaces inside the quote are preserved "
                  //  StrValG =  This works too and spaces are trimmed away
                  //  StrValH =
                  //  ----------------------------------------------------------------
                  //12oClocker super lean and mean INI file parser (with section support)
                  //set section to 0 to disable section support
                  //returns TRUE if we were able to extract a string into ret value
                  //NextSection is a char* pointer, will be set to zero if no next section is found
                  //will be set to pointer of next section if it was found.
                  //use it like this... char* NextSection = 0;  GrabIniValue(X,X,X,X,X,&NextSection);
                  //buf is data to parse, ret is the user supplied return buffer
                  BOOL GrabIniValue(char* buf, const char* section, const char* valname, char* ret, int retbuflen, char** NextSection)
                  {
                      if(!buf){*ret=0; return FALSE;}
                  
                      char* s = buf; //search starts at "s" pointer
                      char* e = 0;   //end of section pointer
                  
                      //find section
                      if(section)
                      {
                          int L = strlen(section);
                          SearchAgain1:
                          s = strstr(s,section); if(!s){*ret=0; return FALSE;}    //find section
                          if(s > buf && (*(s-1))!='\n'){s+=L; goto SearchAgain1;} //section must be at begining of a line!
                          s+=L;                                                   //found section, skip past section name
                          while(*s!='\n'){s++;} s++;                              //spin until next line, s is now begining of section data
                          e = strstr(s,"\n[");                                    //find begining of next section or end of file
                          if(e){*e=0;}                                            //if we found begining of next section, null the \n so we don't search past section
                          if(NextSection)                                         //user passed in a NextSection pointer
                          { if(e){*NextSection=(e+1);}else{*NextSection=0;} }     //set pointer to next section
                      }
                  
                      //restore char at end of section, ret=empty_string, return FALSE
                      #define RESTORE_E     if(e){*e='\n';}
                      #define SAFE_RETURN   RESTORE_E;  (*ret)=0;  return FALSE
                  
                      //find valname
                      int L = strlen(valname);
                      SearchAgain2:
                      s = strstr(s,valname); if(!s){SAFE_RETURN;}             //find valname
                      if(s > buf && (*(s-1))!='\n'){s+=L; goto SearchAgain2;} //valname must be at begining of a line!
                      s+=L;                                                   //found valname match, skip past it
                      while(*s==' ' || *s == '\t'){s++;}                      //skip spaces and tabs
                      if(!(*s)){SAFE_RETURN;}                                 //if NULL encounted do safe return
                      if(*s != '='){goto SearchAgain2;}                       //no equal sign found after valname, search again
                      s++;                                                    //skip past the equal sign
                      while(*s==' '  || *s=='\t'){s++;}                       //skip spaces and tabs
                      while(*s=='\"' || *s=='\''){s++;}                       //skip past quotes and ticks
                      if(!(*s)){SAFE_RETURN;}                                 //if NULL encounted do safe return
                      char* E = s;                                            //s is now the begining of the valname data
                      while(*E!='\r' && *E!='\n' && *E!=0){E++;} E--;         //find end of line or end of string, then backup 1 char
                      while(E > s && (*E==' ' || *E=='\t')){E--;}             //move backwards past spaces and tabs
                      while(E > s && (*E=='\"' || *E=='\'')){E--;}            //move backwards past quotes and ticks
                      L = E-s+1;                                              //length of string to extract NOT including NULL
                      if(L<1 || L+1 > retbuflen){SAFE_RETURN;}                //empty string or buffer size too small
                      strncpy(ret,s,L);                                       //copy the string
                      ret[L]=0;                                               //null last char on return buffer
                      RESTORE_E;
                      return TRUE;
                  
                      #undef RESTORE_E
                      #undef SAFE_RETURN
                  }
                  

                  如何使用...示例....

                  char sFileData[] = "[MySection]\r\n"
                  "MyValue1 = 123\r\n"
                  "MyValue2 = 456\r\n"
                  "MyValue3 = 789\r\n"
                  "\r\n"
                  "[MySection]\r\n"
                  "MyValue1 = Hello1\r\n"
                  "MyValue2 = Hello2\r\n"
                  "MyValue3 = Hello3\r\n"
                  "\r\n";
                  char str[256];
                  char* sSec = sFileData;
                  char secName[] = "[MySection]"; //we support sections with same name
                  while(sSec)//while we have a valid sNextSec
                  {
                      //print values of the sections
                      char* next=0;//in case we dont have any sucessful grabs
                      if(GrabIniValue(sSec,secName,"MyValue1",str,sizeof(str),&next)) { printf("MyValue1 = [%s]\n",str); }
                      if(GrabIniValue(sSec,secName,"MyValue2",str,sizeof(str),0))     { printf("MyValue2 = [%s]\n",str); }
                      if(GrabIniValue(sSec,secName,"MyValue3",str,sizeof(str),0))     { printf("MyValue3 = [%s]\n",str); }
                      printf("\n");
                      sSec = next; //parse next section, next will be null if no more sections to parse
                  }
                  

                  【讨论】:

                    【解决方案13】:

                    我最终使用了 inipp,这个帖子中没有提到。

                    https://github.com/mcmtroffaes/inipp

                    是仅获得 MIT 许可的标头实现,非常简单,可以添加到项目中并使用 4 行代码。

                    【讨论】:

                      猜你喜欢
                      • 2013-07-06
                      • 2023-03-17
                      • 2012-09-30
                      • 2017-02-11
                      • 1970-01-01
                      • 2012-10-11
                      • 2020-03-14
                      相关资源
                      最近更新 更多