¿Cuál es la forma más sencilla de analizar un archivo INI en C ++?

89

Estoy tratando de analizar un archivo INI usando C ++. ¿Algún consejo sobre cuál es la mejor manera de lograrlo? ¿Debo usar las herramientas API de Windows para el procesamiento de archivos INI (con las que no estoy familiarizado), una solución de código abierto o intentar analizarlo manualmente?

conmulligan
fuente

Respuestas:

112

Puede utilizar las funciones de la API de Windows, como GetPrivateProfileString () y GetPrivateProfileInt () .

Joel Spolsky
fuente
4
GetPrivateProfileInt () y otras funciones no son recomendadas por MSDN, porque son obsoletas y aún se proporcionan solo para compatibilidad básica con sistemas más antiguos de 16 bits. En lugar de eso, use otro enfoque. msdn.microsoft.com/en-us/library/windows/desktop/…
Zdeno Pavlik
Son obsoletos porque MS no quiere que uses más archivos ini, siguen siendo ideales si realmente quieres leer o escribir dichos archivos.
Neil
114

Si necesita una solución multiplataforma, pruebe la biblioteca de opciones de programa de Boost .

Adam Mitz
fuente
1
Sugeriría esta biblioteca también
varnie
22
este es el camino a seguir, no entiendo por qué la gente simplemente vota a favor de una respuesta no tan general.
Ramadheer Singh
17
@Gollum, parece que Windows es una dependencia determinada. Usar la biblioteca de Opciones de programa significa asumir otra dependencia. A veces eso no es gran cosa, a veces lo es.
IJ Kennedy
5
@malat Estoy confundido, ¿no mencioné el voto negativo?
sjdowling
2
Está tratando de leer un archivo INI existente. Usar boost no es una respuesta porque está usando un formato similar a INI.
Lothar
16

Si ya está usando Qt

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

Entonces lee un valor

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

Hay muchos otros convertidores que convierten sus valores INI en tipos estándar y tipos Qt. Consulte la documentación de Qt sobre QSettings para obtener más información.

Dat Chu
fuente
No está mal, aunque si realiza cambios, los guardan de nuevo en el archivo .ini sin realmente decírselo (es decir, el destructor llama sync(), lo que puede ser una sorpresa) y eso destruye los comentarios y el orden en el que se definieron las variables anteriormente ...
Alexis Wilke
16

Yo uso SimpleIni . Es multiplataforma.

Harold Ekstrom
fuente
SimpleIni ahora está alojado en Github.
Richard Ye
8

esta pregunta es un poco antigua, pero publicaré mi respuesta. He probado varias clases INI (puede verlas en mi sitio web ) y también uso simpleIni porque quiero trabajar con archivos INI tanto en Windows como en winCE. GetPrivateProfileString () de Windows solo funciona con el registro en winCE.

Es muy fácil de leer con simpleIni. Aquí hay un ejemplo:

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

inih es un analizador ini simple escrito en C, también viene con un contenedor C ++. Uso de ejemplo:

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

El autor también tiene una lista de bibliotecas existentes aquí .

nimcap
fuente
4

¿Ha probado libconfig ; sintaxis muy similar a JSON. Lo prefiero a los archivos de configuración XML.

Christopher Lightfoot
fuente
3

Si está interesado en la portabilidad de la plataforma, también puede probar Boost.PropertyTree. Admite ini como formato de persistencia, aunque el árbol de propiedades solo puede tener 1 nivel de profundidad.

gast128
fuente
2

A menos que planee hacer que la aplicación sea multiplataforma, usar las llamadas a la API de Windows sería la mejor manera de hacerlo. Simplemente ignore la nota en la documentación de la API sobre que se proporciona solo para la compatibilidad de aplicaciones de 16 bits.

crashmstr
fuente
0

Sé que esta pregunta es muy antigua, pero la encontré porque necesitaba algo multiplataforma para Linux, win32 ... Escribí la función a continuación, es una función única que puede analizar archivos INI, espero que otros la encuentren útil.

reglas y advertencias: buf para analizar debe ser una cadena terminada en NULL. Cargue su archivo ini en una cadena de matriz de caracteres y llame a esta función para analizarlo. los nombres de las secciones deben tener [] corchetes alrededor, como este [MySection], también los valores y las secciones deben comenzar en una línea sin espacios iniciales. Analizará archivos con Windows \ r \ n o con finales de línea \ n Linux. Los comentarios deben usar # o // y comenzar en la parte superior del archivo, ningún comentario debe mezclarse con los datos de entrada INI. Las comillas y las marcas se recortan de ambos extremos de la cadena de retorno. Los espacios solo se recortan si están fuera del presupuesto. No es necesario que las cadenas tengan comillas y los espacios en blanco se recortan si faltan comillas. También puede extraer números u otros datos, por ejemplo, si tiene un flotante, simplemente realice un atof (ret) en el búfer 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
}

Cómo usar ... ejemplo ...

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 en punto
fuente
0

Terminé usando inipp que no se menciona en este hilo.

https://github.com/mcmtroffaes/inipp

Era una implementación de encabezado con licencia del MIT que era lo suficientemente simple como para agregar a un proyecto y 4 líneas para usar.

usuario1867382
fuente