What Is the Easiest Way to Parse an Ini File in C++

What is the easiest way to parse an INI File in C++?

You can use the Windows API functions, such as GetPrivateProfileString() and GetPrivateProfileInt().

INI file parser for C

This link may shed some light on the matter (written by the guy that authored inih as mentioned by Zagorulkin Dmitry) - I have used minIni and been happy with it..

What is the easiest way to parse an INI File in C?

What problems are you having with using iniparser? I just tried it. I first did make in the iniparser directory, and the code was built. To use the library, I did the following:


gcc test.c ./libiniparser.a

This was because I had created the test program in the same directory as the library. When you include iniparser.h in C++, make sure to do the following:

extern "C"
{
#include "src/iniparser.h"
}

Parsing INI file in C - how to store sections and its' keys and values?

You are working down the correct path, but there are a few things that you must approach differently if you want ensure things work correctly. If you take nothing else from this answer, learn that you cannot use any input or parsing function without checking the return (that applies to virtually every function you use, unless the operation of code that follows does not depend on the result -- like just printing values) Also, you never use while (!feof(fpointer)), e.g. see: Why is while ( !feof (file) ) always wrong?

Now, how to approach the problem. First, if you need a constant for your array size, then #define a constant or use a global enum. For example, for my sect, inisect, key, inikey and val buffers I would define SPLTC and then for my line buffer, I define MAXC, e.g.

#define SPLTC 128       /* if you need a constant, #define one (or more) */
#define MAXC 256

Depending on whether you need to be -ansi or c89/90 compatible, declare your variables before any operations, e.g.

int main (int argc, char **argv) {

char buf[MAXC], sect[SPLTC], inisect[SPLTC], key[SPLTC], inikey[SPLTC], val[SPLTC];
FILE *fp = NULL;

Then the first thing you will do is validate that sufficient arguments were provided on the command line:

    if (argc < 3) { /* validate 2 arguments provided */
fprintf (stderr,
"error: insufficient number of arguments\n"
"usage: %s file.ini section.key\n", argv[0]);
return 1;
}

Next you will open your file and validate that it is open for reading:

    /* open/validate file open for reading */
if ((fp = fopen (argv[1], "r")) == NULL) {
fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
perror ("fopen");
return 1;
}

Then split your section.key argument argv[2] into sect and key and validate the separation:

    /* split section.key into sect & key */
if (sscanf (argv[2], " %127[^.]. %127s", sect, key) != 2) {
fputs ("error: invalid section.key\n", stderr);
return 1;
}

Now enter your read loop to find your section in the file (you always control the loop with the return of the read function itself):

    while (fgets (buf, MAXC, fp)) {                 /* read each line */
if (buf[0] == '[') { /* is first char '[]'? */
if (sscanf (buf, " [%127[^]]", inisect) == 1) { /* parse section */
if (strcmp (sect, inisect) == 0) /* does it match 2nd arg? */
break; /* if so break loop */
}
}
}

How do you check that the section was found? You can keep a flag-variable as you have done with right_section, or... think about where you would be in the file if your section wasn't found? You would be at EOF. So now you can correctly check feof(fp), e.g.

    if (feof (fp)) {    /* if file stream at EOF, section not found */
fprintf (stderr, "error: EOF encountered before section '%s' found.\n",
sect);
return 1;
}

If you haven't exited due to not finding your section (meaning you got to this point in the code), just read each line validating a separation into inikey and val (if the validation fails -- you have read all the key/val pairs in that section without a match) If you find the key match during your read of the section success you have your inikey and val. If you complete the loop without a match you can check if you issue an error, and if you reach EOF without a match, you can again check feof(fp) after the loop, e.g.

    while (fgets (buf, MAXC, fp)) {                 /* continue reading lines */
/* parse key & val from line */
if (sscanf (buf, " %127s = %127s", inikey, val) != 2) { /* if not key & val */
fprintf (stderr, "error: end of section '%s' reached "
"with no matching key found.\n", sect);
return 1;
}
if (strcmp (key, inikey) == 0) { /* does key match? */
printf ("section : %s\n key : %s\n val : %s\n", sect, key, val);
break;
}
}

if (feof (fp)) { /* if file stream at EOF, key not found */
fprintf (stderr, "error: EOF encountered before key '%s' found.\n",
argv[3]);
return 1;
}

That's basically it. If you put it altogether you have:

#include <stdio.h>
#include <string.h>

#define SPLTC 128 /* if you need a constant, #define one (or more) */
#define MAXC 256

int main (int argc, char **argv) {

char buf[MAXC], sect[SPLTC], inisect[SPLTC], key[SPLTC], inikey[SPLTC], val[SPLTC];
FILE *fp = NULL;

if (argc < 3) { /* validate 2 arguments provided */
fprintf (stderr,
"error: insufficient number of arguments\n"
"usage: %s file.ini section.key\n", argv[0]);
return 1;
}

/* open/validate file open for reading */
if ((fp = fopen (argv[1], "r")) == NULL) {
fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
perror ("fopen");
return 1;
}

/* split section.key into sect & key */
if (sscanf (argv[2], " %127[^.]. %127s", sect, key) != 2) {
fputs ("error: invalid section.key\n", stderr);
return 1;
}

while (fgets (buf, MAXC, fp)) { /* read each line */
if (buf[0] == '[') { /* is first char '[]'? */
if (sscanf (buf, " [%127[^]]", inisect) == 1) { /* parse section */
if (strcmp (sect, inisect) == 0) /* does it match 2nd arg? */
break; /* if so break loop */
}
}
}

if (feof (fp)) { /* if file stream at EOF, section not found */
fprintf (stderr, "error: EOF encountered before section '%s' found.\n",
sect);
return 1;
}

while (fgets (buf, MAXC, fp)) { /* continue reading lines */
/* parse key & val from line */
if (sscanf (buf, " %127s = %127s", inikey, val) != 2) { /* if not key & val */
fprintf (stderr, "error: end of section '%s' reached "
"with no matching key found.\n", sect);
return 1;
}
if (strcmp (key, inikey) == 0) { /* does key match? */
printf ("section : %s\n key : %s\n val : %s\n", sect, key, val);
break;
}
}

if (feof (fp)) { /* if file stream at EOF, key not found */
fprintf (stderr, "error: EOF encountered before key '%s' found.\n",
argv[3]);
return 1;
}
}

Example Use/Output

Finding valid section/key combinations:

$ ./bin/readini dat/test.ini section2.key3
section : section2
key : key3
val : vaule3

$ /bin/readini dat/test.ini section2.key5
section : section2
key : key5
val : value5

$ ./bin/readini dat/test.ini section1.key2
section : section1
key : key2
val : value2

Attempts to find invalid section/key combinations.

$ ./bin/readini dat/test.ini section1.key3
error: end of section 'section1' reached with no matching key found.

$ ./bin/readini dat/test.ini section2.key8
error: EOF encountered before key 'key8' found.

Look things over and let me know if you have further questions.

How to read from an .ini file?

Assuming you are using Windows, you can work this way:

First include Windows.h

#include <Windows.h>

On your function, create a LPCSTR (heh!? Really? Yeah.)

LPCSTR ini = "C:\\config.ini";

And call GetPrivateProfileString:

char returnValue[100];
GetPrivateProfileString("states", "title", 0, returnValue, 100, ini);

C - Read from .INI file and pass values to vars

The function strtok take a string only the first time it gets called. All
subsequent calls must be passed with NULL

man strtok

#include <string.h>
char *strtok(char *str, const char *delim);

DESCRIPTION

The strtok() function breaks a string into a sequence of zero or more nonempty tokens.
On the first call to strtok(), the string to be parsed should be specified in str.
In each subsequent call that should parse the same string, str must be NULL.

Example:

char line[] = "a,b,c,d,e,f\n"; // to simulate an fgets line
char *token = strtok(line, ",\n"); // you can add \n to the separator
// to get rid of the \n at the end

puts(token); // prints a
while(token = strtok(NULL, ",\n"))
puts(token); // prints b then c etc..

Keep in mind that strtok modifies the source, this is going to fail:

strtok("a,b,c", ",");

because string literals are not modifiable. In that case you have to make a copy
to a char[] or a dynamic allocated char*.

If you need to have the source intact after the parsing, then you definitively
need to make a copy.

In your code you do:

printf("%d", token[0]);

That's not incorrect but perhaps not what you want to do. This line doesn't
print the first character, it prints the ascii value of the first character.

This

printf("%c", token[0]);

will print the first character.

Also you are doing

CONFIG read_config(char *argv[]) {
...
CONFIG config;
return config;
}

You are returning an uninitialized CONFIG object, you are ignoring the parsing
and nothing is set in your config object.

Your parsing is also a little bit strange.

for (int i = 0; i != '\0'; i++)

The loop exits immediately because 0 == '\0'! I don't understand what you are
trying to do with it.

I would first create a helper function to populate the values of the config, see
set_config_val. Then you can parse it like this:

CONFIG read_config(char *argv[]) {
...

const char *delim = "=\n";
CONFIG config;

while (fgets(str, MAXSTR, fp) != NULL) {

if(strchr(str, '='))
{
char varname[100];
int value;

token = strtok(line, delim);
strcpy(varname, token);

token = strtok(NULL, delim);
value = atoi(token);

set_config_val(&config, varname, value);
} else {
fprintf(stderr, "Skipping line, no = found");
}

}

fclose(fp);
return config;
}


void set_config_val(CONFIG *config, const char *key, int val)
{
if(config == NULL)
return;

if(strcmp(key, "xdim") == 0)
config->xdim = val;
else if(strcmp(key, "ydim") == 0)
config->ydim = val;
...
}

How to parse ini file with Boost

You can also use Boost.PropertyTree to read .ini files:

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>

...

boost::property_tree::ptree pt;
boost::property_tree::ini_parser::read_ini("config.ini", pt);
std::cout << pt.get<std::string>("Section1.Value1") << std::endl;
std::cout << pt.get<std::string>("Section1.Value2") << std::endl;


Related Topics



Leave a reply



Submit