Jaki jest najłatwiejszy sposób przeanalizowania pliku INI w C ++?

89

Próbuję przeanalizować plik INI przy użyciu C ++. Jakieś wskazówki, jak najlepiej to osiągnąć? Czy powinienem używać narzędzi Windows API do przetwarzania plików INI (z którymi jestem całkowicie nieznany), rozwiązania typu open source, czy też próbować analizować je ręcznie?

conmulligan
źródło

Odpowiedzi:

112

Możesz użyć funkcji Windows API, takich jak GetPrivateProfileString () i GetPrivateProfileInt () .

Joel Spolsky
źródło
4
GetPrivateProfileInt () i inne funkcje nie są zalecane przez MSDN, ponieważ są przestarzałe i nadal są udostępniane tylko w celu zapewnienia zgodności ze starszymi, 16-bitowymi systemami. Zamiast tego użyj innego podejścia. msdn.microsoft.com/en-us/library/windows/desktop/…
Zdeno Pavlik
Są przestarzałe, ponieważ MS nie chce już, abyś używał plików ini, nadal są idealne, jeśli chcesz czytać lub pisać takie pliki.
Neil
114

Jeśli potrzebujesz rozwiązania wieloplatformowego, wypróbuj bibliotekę opcji programu Boost .

Adam Mitz
źródło
1
proponuję również tę bibliotekę
varnie
21
to jest droga do zrobienia, nie rozumiem, dlaczego ludzie po prostu popierają nie tak ogólną odpowiedź.
Ramadheer Singh
17
@Gollum, wygląda na to, że Windows jest podaną zależnością. Korzystanie z biblioteki opcji programu oznacza przyjęcie innej zależności. Czasami to nic wielkiego, czasami tak jest.
IJ Kennedy
5
@malat Jestem zdezorientowany, nie wspomniałem o obniżaniu głosów?
sjdowling
2
Próbuje odczytać istniejący plik INI. Użycie wzmocnienia nie jest odpowiedzią, ponieważ używa formatu podobnego do INI.
Lothar
16

Jeśli już używasz Qt

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

Następnie odczytaj wartość

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

Istnieje wiele innych konwerterów, które konwertują wartości INI zarówno na typy standardowe, jak i typy Qt. Więcej informacji można znaleźć w dokumentacji Qt dotyczącej QSettings.

Dat Chu
źródło
Nieźle, chociaż jeśli wprowadzisz zmiany, zapisują je z powrotem do pliku .ini bez mówienia Ci o tym (np. Wywołanie destruktora sync(), co może być niespodzianką), a to niszczy komentarze i kolejność, w jakiej zmienne były zdefiniowane wcześniej ...
Alexis Wilke
16

Używam SimpleIni . Jest wieloplatformowy.

Harold Ekstrom
źródło
SimpleIni jest teraz hostowane na Githubie.
Richard Ye
8

to pytanie jest trochę stare, ale opublikuję swoją odpowiedź. Testowałem różne klasy INI (możesz je zobaczyć na mojej stronie ), a także używam simpleIni, ponieważ chcę pracować z plikami INI zarówno w oknach, jak i winCE. Funkcja GetPrivateProfileString () systemu Windows działa tylko z rejestrem na winCE.

Dzięki simpleIni jest bardzo łatwy do odczytania. Oto przykład:

#include "SimpleIni\SimpleIni.h"    
CSimpleIniA ini;
ini.SetUnicode();
ini.LoadFile(FileName);
const char * pVal = ini.GetValue(section, entry, DefaultStr);
Mikrofon
źródło
6

inih to prosty parser ini napisany w C, zawiera również opakowanie C ++. Przykładowe użycie:

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

Autor ma również listę istniejących bibliotek tutaj .

nimcap
źródło
4

Czy próbowałeś libconfig ; składnia bardzo podobna do JSON. Wolę to od plików konfiguracyjnych XML.

Christopher Lightfoot
źródło
3

Jeśli interesuje Cię przenośność platformy, możesz również wypróbować Boost.PropertyTree. Obsługuje ini jako format trwałości, chociaż drzewo właściwości może mieć tylko 1 poziom głębokości.

gast128
źródło
2

O ile nie planujesz tworzyć aplikacji międzyplatformowej, najlepszym sposobem byłoby użycie wywołań interfejsu API systemu Windows. Po prostu zignoruj ​​uwagę w dokumentacji interfejsu API, że jest udostępniana tylko w celu zapewnienia zgodności aplikacji 16-bitowych.

crashmstr
źródło
0

Wiem, że to pytanie jest bardzo stare, ale natknąłem się na to, ponieważ potrzebowałem czegoś międzyplatformowego dla linuxa, win32 ... Napisałem funkcję poniżej, jest to pojedyncza funkcja, która może analizować pliki INI, mam nadzieję, że inni uznają ją za przydatną.

reguły i zastrzeżenia: buf do przeanalizowania musi być łańcuchem zakończonym znakiem NULL. Załaduj swój plik ini do tablicy znaków i wywołaj tę funkcję, aby go przeanalizować. nazwy sekcji muszą być otoczone nawiasami kwadratowymi [], na przykład [MySection], a także wartości i sekcje muszą zaczynać się w wierszu bez początkowych spacji. Sparsuje pliki z końcówkami linii Windows \ r \ n lub Linux \ n. Komentarze powinny zawierać # lub // i zaczynać się na górze pliku, żadne komentarze nie powinny być mieszane z danymi wejściowymi INI. Cytaty i znaczniki są obcinane z obu końców zwracanego ciągu. Spacje są przycinane tylko wtedy, gdy znajdują się poza ofertą. Łańcuchy nie muszą mieć cudzysłowów, a białe znaki są obcinane, jeśli brakuje cudzysłowów. Możesz także wyodrębnić liczby lub inne dane, na przykład jeśli masz zmiennoprzecinkową, po prostu wykonaj atof (ret) w buforze 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
}

Jak używać ... przykład ....

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
źródło
0

Skończyło się na korzystaniu z inipp, o którym nie wspomniano w tym wątku.

https://github.com/mcmtroffaes/inipp

Była to implementacja z licencją MIT tylko na nagłówek, która była wystarczająco prosta, aby dodać do projektu i 4 linie do użycia.

user1867382
źródło