Was ist der einfachste Weg, eine INI-Datei in C ++ zu analysieren?

89

Ich versuche, eine INI-Datei mit C ++ zu analysieren. Irgendwelche Tipps, wie dies am besten erreicht werden kann? Sollte ich die Windows-API-Tools für die INI-Dateiverarbeitung (mit der ich völlig unbekannt bin), eine Open-Source-Lösung verwenden oder versuchen, sie manuell zu analysieren?

conmulligan
quelle

Antworten:

112

Sie können die Windows-API-Funktionen wie GetPrivateProfileString () und GetPrivateProfileInt () verwenden .

Joel Spolsky
quelle
4
GetPrivateProfileInt () und andere Funktionen werden von MSDN nicht empfohlen, da sie veraltet sind und nur aus Gründen der Kompatibilität mit älteren 16-Bit-Systemen bereitgestellt werden. Verwenden Sie stattdessen einen anderen Ansatz. msdn.microsoft.com/en-us/library/windows/desktop/…
Zdeno Pavlik
Sie sind veraltet, weil MS nicht mehr möchte, dass Sie INI-Dateien verwenden. Sie sind dennoch ideal, wenn Sie solche Dateien tatsächlich lesen oder schreiben möchten.
Neil
113

Wenn Sie eine plattformübergreifende Lösung benötigen, probieren Sie die Programmoptionsbibliothek von Boost aus .

Adam Mitz
quelle
1
Ich würde diese Bibliothek auch
vorschlagen
21
Dies ist der richtige Weg, ich verstehe nicht, warum die Leute nur nicht so allgemeine Antworten stimmen.
Ramadheer Singh
17
@Gollum, es klingt so, als ob Windows eine gegebene Abhängigkeit ist. Wenn Sie die Programmoptionsbibliothek verwenden, übernehmen Sie eine andere Abhängigkeit. Manchmal ist das keine große Sache, manchmal ist es das.
IJ Kennedy
5
@malat Ich bin verwirrt, ich habe Downvoting nicht erwähnt?
sjdowling
2
Er versucht, eine vorhandene INI-Datei zu lesen. Die Verwendung von Boost ist keine Antwort, da ein INI-ähnliches Format verwendet wird.
Lothar
16

Ich benutze SimpleIni . Es ist plattformübergreifend.

Harold Ekstrom
quelle
SimpleIni wird jetzt in Github gehostet.
Richard Ye
15

Wenn Sie bereits Qt verwenden

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

Dann lesen Sie einen Wert

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

Es gibt eine Reihe anderer Konverter, die Ihre INI-Werte sowohl in Standardtypen als auch in Qt-Typen konvertieren. Weitere Informationen finden Sie in der Qt-Dokumentation zu QSettings.

Dat Chu
quelle
Nicht schlecht, aber wenn Sie Änderungen vornehmen, speichern sie diese zurück in der INI-Datei, ohne es Ihnen wirklich mitzuteilen (dh die Destruktoraufrufe sync(), was eine Überraschung sein kann), und dies zerstört die Kommentare und die Reihenfolge, in der zuvor Variablen definiert wurden ...
Alexis Wilke
8

Diese Frage ist etwas alt, aber ich werde meine Antwort posten. Ich habe verschiedene INI-Klassen getestet (Sie können sie auf meiner Website sehen ) und ich verwende auch simpleIni, weil ich mit INI-Dateien sowohl unter Windows als auch unter winCE arbeiten möchte. GetPrivateProfileString () von Window funktioniert nur mit der Registrierung unter winCE.

Mit simpleIni ist es sehr einfach zu lesen. Hier ist ein Beispiel:

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

inih ist ein einfacher ini-Parser, der in C geschrieben ist. Er wird auch mit einem C ++ - Wrapper geliefert . Anwendungsbeispiel:

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

Der Autor hat auch eine Liste der vorhandenen Bibliotheken hier .

Nimcap
quelle
4

Haben Sie versucht , libconfig ; sehr JSON-ähnliche Syntax. Ich bevorzuge es gegenüber XML-Konfigurationsdateien.

Christopher Lightfoot
quelle
3

Wenn Sie an Plattformportabilität interessiert sind, können Sie auch Boost.PropertyTree ausprobieren. Es unterstützt INI als Persistenzformat, obwohl der Eigenschaftsbaum nur 1 Ebene tief sein kann.

gast128
quelle
2

Wenn Sie nicht vorhaben, die App plattformübergreifend zu gestalten, ist die Verwendung der Windows-API-Aufrufe der beste Weg. Ignorieren Sie einfach den Hinweis in der API-Dokumentation, dass er nur aus Gründen der 16-Bit-App-Kompatibilität bereitgestellt wird.

Crashmstr
quelle
0

Ich weiß, dass diese Frage sehr alt ist, aber ich bin darauf gestoßen, weil ich etwas plattformübergreifendes für Linux brauchte, win32 ... Ich habe die folgende Funktion geschrieben, es ist eine einzelne Funktion, die INI-Dateien analysieren kann, hoffentlich finden andere sie nützlich.

Regeln & Vorsichtsmaßnahmen: buf to parse muss eine NULL-terminierte Zeichenfolge sein. Laden Sie Ihre INI-Datei in eine char-Array-Zeichenfolge und rufen Sie diese Funktion auf, um sie zu analysieren. Abschnittsnamen müssen mit [] Klammern versehen sein, z. B. [MySection]. Außerdem müssen Werte und Abschnitte in einer Zeile ohne führende Leerzeichen beginnen. Es werden Dateien mit Windows \ r \ n oder mit Linux \ n Zeilenenden analysiert. Kommentare sollten # oder // verwenden und am Anfang der Datei beginnen. Es sollten keine Kommentare mit INI-Eintragsdaten gemischt werden. Anführungszeichen und Häkchen werden an beiden Enden der Rückgabezeichenfolge abgeschnitten. Leerzeichen werden nur gekürzt, wenn sie außerhalb des Zitats liegen. Zeichenfolgen müssen keine Anführungszeichen enthalten, und Leerzeichen werden gekürzt, wenn Anführungszeichen fehlen. Sie können auch Zahlen oder andere Daten extrahieren, z. B. wenn Sie einen Float haben, führen Sie einfach ein atof (ret) für den ret-Puffer durch.

//  -----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
}

Wie benutzt man ... Beispiel ....

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
}
12 Uhr
quelle
0

Am Ende habe ich inipp verwendet, was in diesem Thread nicht erwähnt wird.

https://github.com/mcmtroffaes/inipp

War eine MIT-lizenzierte Header-Implementierung, die einfach genug war, um sie zu einem Projekt hinzuzufügen und 4 Zeilen zu verwenden.

user1867382
quelle