Qual é a maneira mais fácil de analisar um arquivo INI em C ++?

89

Estou tentando analisar um arquivo INI usando C ++. Alguma dica sobre qual é a melhor maneira de conseguir isso? Devo usar as ferramentas de API do Windows para processamento de arquivo INI (com as quais não estou totalmente familiarizado), uma solução de código aberto ou tentar analisá-la manualmente?

Conmulligan
fonte

Respostas:

112

Você pode usar as funções da API do Windows, como GetPrivateProfileString () e GetPrivateProfileInt () .

Joel Spolsky
fonte
4
GetPrivateProfileInt () e outras funções não são recomendadas pelo MSDN, porque são obsoletas e ainda fornecidas apenas para compatibilidade básica com sistemas mais antigos de 16 bits. Em vez disso, use outra abordagem. msdn.microsoft.com/en-us/library/windows/desktop/…
Zdeno Pavlik
Eles estão obsoletos porque a MS não quer que você use mais arquivos ini, eles ainda são ideais se você realmente deseja ler ou gravar tais arquivos.
Neil
113

Se você precisar de uma solução de plataforma cruzada, experimente a biblioteca de opções de programa do Boost .

Adam Mitz
fonte
1
eu sugeriria esta biblioteca também
varnie
21
este é o caminho a percorrer, não entendo por que as pessoas apenas votam a favor de uma resposta não tão geral.
Ramadheer Singh
17
@Gollum, parece que o Windows é uma determinada dependência. Usar a biblioteca de opções do programa significa assumir outra dependência. Às vezes isso não é grande coisa, às vezes é.
IJ Kennedy
5
@malat Estou confuso, não mencionei o downvoting?
noite de
2
Ele está tentando ler um arquivo INI existente. Usar boost não é uma resposta porque está usando um formato semelhante ao INI.
Lothar
16

Eu uso SimpleIni . É multiplataforma.

Harold Ekstrom
fonte
SimpleIni agora está hospedado no Github.
Richard Ye
15

Se você já está usando Qt

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

Então leia um valor

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

Existem vários outros conversores que convertem seus valores INI em tipos padrão e tipos Qt. Veja a documentação Qt em QSettings para mais informações.

Dat Chu
fonte
Nada mal, embora, se você fizer alterações, eles as salvem de volta no arquivo .ini sem realmente avisá-lo (ou seja, as chamadas do destruidor sync(), o que pode ser uma surpresa) e isso destrói os comentários e a ordem em que as variáveis ​​foram definidas anteriormente ...
Alexis Wilke
8

essa pergunta é um pouco antiga, mas vou postar minha resposta. Eu testei várias classes INI (você pode vê-las no meu site ) e também uso o simpleIni porque quero trabalhar com arquivos INI no Windows e no winCE. GetPrivateProfileString () do Windows funciona apenas com o registro no winCE.

É muito fácil de ler com o simpleIni. Aqui está um exemplo:

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

inih é um analisador ini simples escrito em C, que também vem com um wrapper C ++. Exemplo de uso:

#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";

O autor também tem uma lista de bibliotecas existentes aqui .

nimcap
fonte
4

Você já tentou libconfig ; sintaxe muito semelhante a JSON. Eu prefiro isso em vez de arquivos de configuração XML.

Christopher Lightfoot
fonte
3

Se você estiver interessado na portabilidade da plataforma, também pode tentar Boost.PropertyTree. Ele suporta ini como formato de persistência, embora a árvore de propriedades possa ter apenas 1 nível de profundidade.

gast128
fonte
2

A menos que você planeje tornar o aplicativo multiplataforma, usar as chamadas de API do Windows seria o melhor caminho a seguir. Basta ignorar a observação na documentação da API sobre o fornecimento apenas para compatibilidade de aplicativos de 16 bits.

crashmstr
fonte
0

Sei que essa pergunta é muito antiga, mas me deparei com ela porque precisava de algo multiplataforma para linux, win32 ... Eu escrevi a função abaixo, é uma função única que pode analisar arquivos INI, espero que outros achem útil.

regras e advertências: buf a analisar deve ser uma string terminada em NULL. Carregue seu arquivo ini em uma string de array char e chame esta função para analisá-lo. os nomes das seções devem ter [] colchetes ao redor deles, como este [MySection], também os valores e as seções devem começar em uma linha sem espaços à esquerda. Ele analisará arquivos com Windows \ r \ n ou Linux \ n terminações de linha. Os comentários devem usar # ou // e começar no início do arquivo, nenhum comentário deve ser misturado com os dados de entrada INI. Aspas e tiques são cortados em ambas as extremidades da string de retorno. Os espaços são cortados apenas se estiverem fora da citação. As strings não precisam ter aspas e os espaços em branco serão cortados se as aspas estiverem faltando. Você também pode extrair números ou outros dados, por exemplo, se você tiver um float, basta executar um atof (ret) no buffer 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
}

Como usar ... exemplo ....

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
}
12oclocker
fonte
0

Acabei usando o inipp, que não é mencionado neste tópico.

https://github.com/mcmtroffaes/inipp

Era uma implementação apenas de cabeçalho licenciada pelo MIT que era simples o suficiente para adicionar a um projeto e 4 linhas para usar.

user1867382
fonte